home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-08-10 | 127.0 KB | 4,146 lines |
- /*
- This is an experimental implementation of the signing and checking
- algorithms for a digital signature based on one-way hash functions. The
- new signature method is, for this reason, called a "hash signature." Many
- methods of generating a digital signature from a one-way hash function are
- feasible, so to distinguish this particular method it is called the
- "Xerox Hash Signature." The Xerox Hash Signature presently includes
- three separate components:
-
- 1.) The Abstract Xerox Hash Signature. This includes the software
- which defines how to use a one-way hash function to generate
- a digital signature, but excludes the software of any particular
- one way hash function. In this implementation, the Abstract
- Xerox Hash Signature specifically excludes both MD4 and Snefru.
- The Abstract Xerox Hash Signature specifies how to generate
- a digital signature given a one-way hash function, but does
- NOT specify any particular signature system, nor any particular
- one-way hash function.
-
- 2.) The one-way hash function Snefru (4 passes).
-
- 3.) The one-way hash function MD4.
-
- * Addendum dated 91.11.13: since the original implementation of this
- * system, MD5 has been made available and MD4 is no longer recommended.
- * Further, Snefru with 8 passes has been made available, and Snefru
- * with 4 passes is no longer recommended. For these and various other
- * reasons, the present system should be viewed as a experimental, and
- * is not intended for production use.
-
- It is convenient to have a name for the combination of these components.
- In general, the term "Xerox Hash Signature" refers to one or more specific
- digital signatures generated by combining the Abstract Xerox Hash Signature
- with one or more specific one-way hash functions. In this particular case,
- the Xerox Hash Signature refers to the coupling of the Abstract Xerox Hash
- Signature with the particular one-way hash functions Snefru or MD4.
-
- In other contexts, the term "Xerox Hash Signature" could be used to
- describe the coupling of the Abstract Xerox Hash Signature with other
- specific one-way hash functions. When it is necessary to discuss a
- specific digital signature method created by coupling the Abstract Xerox
- Hash Signature with some specific one-way hash function, the combination
- can use the name "The Xerox Hash Signature based on <name of one-way hash
- function>." For example, the present software can be specifically described
- as the Xerox Hash Signature based on Snefru or MD4.
-
- In general, the signature system described by the term "The Xerox Hash
- Signature" will be determined by context. The term "The Abstract Xerox
- Hash Signature" refers specifically to the general method of combining
- an arbitrary one-way hash function into a digital signature system, but
- does not include any particular one-way hash function nor does it define
- or specify any particular implementation of a digital signature. Only
- when the Abstract Xerox Hash Signature is combined with some specific one
- way hash function have we defined a particular digital signature.
-
- Both Snefru and MD4 are available separately. Both Snefru and MD4 are
- accompanied by separate notices. The implementations of Snefru and MD4
- included here have been modified for convenient use with the present
- software, but they are distinct entities. The notices which accompany
- Snefru and MD4 have been reproduced in the appropriate files. Those
- notices do not pertain to the Abstract Xerox Hash Signature, which
- is a separate and distinct software entity and which is covered by
- separate and distinct notices, which follow:
-
- **************************************************************************
- COPYRIGHT (C) 1990 Xerox corporation. All rights reserved.
-
- XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
- MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR
- ANY PARTICULAR PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED
- WARRANTY OF ANY KIND.
-
- This software is being made available for experimental use only. No
- right is granted to use this software for any other purpose. The rights
- (if any) granted for using the individual modules of this software are
- specified by notices appearing in those modules.
-
- Further, in the author's opinion, it is very possible that one or more
- security related bugs exist in this experimental implementation.
- Also note that the routine OpenAndLockTwoFiles has been modified
- to improve portability, but this has created a timing window.
- This should not significantly affect experimental use.
-
- This software may be covered by one or more claims of either or
- both of the following patents:
-
- #4,881,264:
- Digital Signature System and Method
- Based on a Conventional Encryption Function
- Issued: November 14, 1989.
- Inventor: Ralph C. Merkle.
- Patent owned by: Ralph C. Merkle.
- Contact: Ralph C. Merkle
- Xerox PARC
- 3333 Coyote Hill Road
- Palo Alto, CA 94304
- merkle@xerox.com
-
- #4,309,569:
- Method of Providing Digital Signatures
- Issued: January 5, 1982.
- Inventor: Ralph C. Merkle.
- Patent owned by: Stanford University.
- Contact: Joe Koepnick
- Stanford University
- Office of Technology Licensing
- 857 Serra Street, Second Floor
- Stanford, CA 94305-6225.
- koepnick@angelo.Stanford.EDU
-
- XEROX MAKES NO REPRESENTATION WHATSOEVER WITH RESPECT TO THE RELEVANCE
- OF SUCH PATENTS TO THIS SOFTWARE OR WITH RESPECT TO THE VALIDITY OF
- THESE PATENTS. XEROX AUTHORIZES NO USES OF THE ABSTRACT XEROX HASH
- SIGNATURE EXCEPT FOR THOSE THAT ARE STRICTLY LIMITED TO EXPERIMENTAL
- PURPOSES.
-
- These notices must be retained in any copies of any part of this software.
- **************************************************************************
-
- The following papers describe some of the concepts and ideas used in
- this program:
-
- "A Certified Digital Signature" by Ralph C. Merkle, Crypto '89.
- "One Way Hash Functions and DES" by Ralph C. Merkle, Crypto '89.
- "A Fast Software One Way Hash Function" by Ralph C. Merkle, accepted by
- The Journal of Cryptology.
- "A Digital Signature Based On A Conventional Encryption Function" by
- Ralph C. Merkle, Crypto '87.
- "Secrecy, Authentication, and Public Key Systems" by Ralph C. Merkle,
- Ph.D. Thesis, Stanford University Electrical Engineering Dept. 1979.
- "Design Principles for Hash Functions" by Ivan Bjerre Damgard, Crypto '89.
- "Constructing Digital Signatures from a One Way Function" by
- Leslie Lamport, Computer Science Laboratory Technical Report
- October 18, 1979, CSL-98
-
- This is version 1.0d, June 19, 1990.
-
- THE SIGNING ALGORITHM
-
- The signing algorithm accepts, as input,
-
- 1.) An input file <input> to be signed, and an optional short
- ascii "message" that accompanies the signed file.
-
- 2.) The "current auxilliary information" (a file) which keeps track
- of various bookkeeping information. The file is not secret, and
- the information in it can be divulged without compromising the
- security of the signature. Loss or destruction of the aux info file
- is not a catastrophe -- the information in it can be re-computed from
- the machine key file and the secret user key using the "recoverauxinfo"
- command.
-
- 3.) A "user key" entered by the user. The user
- key is not retained by the program after the message has been signed.
-
- 4.) A "machine key" file that holds a "machine key" and the
- authentication path. If the "machine key" is compromised, then
- the security of the system rests solely on the user key. If the
- user key is compromised, then the security of the system rests
- solely on the machine key. Both keys together are required to
- generate the "secret key" that is actually used to sign messages.
-
- The use of two separate keys has two distinct purposes:
- (a) It means that guessing the user key (which is often ill-chosen
- by an unsophisticated or disinterested user) will not compromise
- the machine key or the secret key, thus improving security, and
- (b) It insures that the user will not use the same secret key
- to generate a new secret-key/public-key pair. Generating the
- same secret-key/public-key pair and signing two sets of
- messages with it will seriously compromise security.
-
- The "machine key" file must also be resistant to unauthorized
- modification -- unauthorized changes to the "authentication path"
- specified in this file can compromise security. In most applications,
- it would also be advisable to make the "machine key" unreadable
- by anyone other than those properly authorized.
-
- The signing algorithm produces as output:
-
- 1.) A "signature file." This file is normally .JZ<input file>,
- (e.g., the input file name prefixed with ".JZ").
-
- THE CHECKING ALGORITHM
-
- The checking algorithm re-computes the public key from the signature
- and the signed file. The signed file and message are first hashed
- using a one-way hash function (Snefru 2.0 or MD4, other algorithms
- can be added easily) to 128 bits. This 128-bit hash value is
- then further hashed to 64-bits using a parameterized one-way hash
- function. This 64-bit value is then hashed to compute the
- public key. The public key is then looked up in a public
- directory to determine the signer. If no matching public key
- is found in the public directory, the signer is unknown.
-
- Note that re-computing the public key is idiosyncratic to hash
- signatures. Other signature methods do not re-compute the
- public key, but instead require that the public key be entered
- as a parameter to the signature checking algorithm. This
- difference might be confusing for those familiar with the
- normal methods of checking a signature.
-
- Input for the checking algorithm is:
-
- 1.) An input file <input> whose signature is to be checked.
-
- 2.) The signature file, .JZ<input>.
-
- Output from the checking algorithm is:
-
- 1.) The re-computed public key.
-
-
- The re-computed public key is used to find any matching
- (previously authenticated) public keys present in the public directory.
- If such a match is found, then the program prints out the name
- of the signer and other information taken from the signed message
- and the public entry. If no known public key matches the
- re-computed public key, then a diagnostic is issued.
-
- Clearly, the correctness of the result depends on the validity of
- the entries in the public directory. Various methods of
- safeguarding the public entries have been proposed in the
- literature. A consideration of these methods is beyond the scope
- of these comments.
-
- In general, the security of hash signatures is dependent on the security of
- the underlieing one-way hash function. If that one-way hash function is
- secure, then the signature method is secure. Two one-way hash functions
- suitable for use in this hash signature are: Snefru 2.0 (with 4 passes)
- and MD4. Other one-way hash functions can be easily added to the code.
- The selection of which one way-hash function to use can be based on any of
- several criteria, including performance, perceived security, availability,
- etc.
-
- Snefru 2.0 is available via anonymous FTP from arisia.xerox.com in directory
- /pub/hash. (MD4 is also available in the same directory as a courtesy).
-
- MD4 has not yet received adequate review to establish its security.
- Its security should therefore be viewed with caution at the
- present time (90.04.25).
-
- Further information about MD4 is available from:
- RSA Data Security.
- 10 Twin Dolphin Drive
- Redwood City, CA 94065
- 415-595-8782
- rsa@well.uucp
-
- Either Snefru or MD4 can be
- used in the current hash signature method by setting the "hashMethod"
- variable appropriately. Snefru is set to 4 passes. Setting Snefru
- to 2 passes is known to be insecure (courtesy of the work of Eli Biham).
- This is available by setting the "hashMethod" variable to SNEFRU4_METHOD.
- MD4 can be specified by setting "hashMethod" to MD4_METHOD.
-
- Snefru with 2 passes can be broken. The security of Snefru with 4 passes
- has not yet received adequate analysis and its use must be viewed with
- caution at the present time (90.04.25).
-
- * Addendum added 91.11.13: Snefru with 8 passes and MD5 are both
- * available. For these and other reasons, the present software
- * should be viewed as experimental. Use of the present system
- * for production use is not recommended.
-
- Please note that the constant values that specify different hash
- methods must be unique. For this reason, anyone who wishes to add
- a new method should contact Ralph C. Merkle to insure that the
- constant value selected does not conflict with other values. In
- any event, constants below 100 (decimal) are reserved for use by
- Xerox.
-
- Other one - way hash functions are known. DES-based one-way
- hash functions could be used, but the inclusion of DES source
- code here might create export issues. DES-based one way hash
- functions are significantly slower than the two functions
- specifically designed for software implementation, Snefru and MD4;
- on the other hand it is reasonable to place greater confidence
- in the security of the recent IBM proposal based on DES.
- (Note that the security of other DES-based one-way hash
- functions should be viewed with great caution). Other one-way hash
- functions known to the author at this time are either significantly
- less efficient or are still subject to serious reservations about
- security.
-
- * Addendum added 91.11.13: it is expected that NIST will soon
- * adopt a one way hash function. This hash function could be
- * adopted as the basis for the hash signature, in which case the
- * security of the system would depend on the security of the NIST
- * provided one-way hash function (modulo the fact that it is likely
- * that some security-related bugs exist in this implementation).
-
- The two values "securitySize" and "parameterSize"
- directly control the security that is provided by the hash
- signature. "securitySize" is the number of 32 - bit words that
- are used to hold security-critical values. "parameterSize" is
- the number of 32-bit words used to hold the security-related
- "parameter" values. It is expected that securitySize will
- either be 2 (providing 64 bits of effective security) or 3
- (providing 96 bits of effective security). The value of
- parameterSize will usually be 2 (providing 2 ** 64 possible parameters).
- Note that "parameters" to the one-way hash function need not be
- randomly selected from a large space. However, different applications
- of the one-way hash function should normally use different values
- for the parameter. Thus, parameter values might start at 1 and proceed
- systematically through 2, 3, 4, etc. Accidental re-use of the same
- parameter value has only a minor impact on security, but widespread
- re-use of the same parameter value would substantially reduce security.
-
- Setting both securitySize and parameterSize to 2 (64 bits) should
- provide a level of security sufficient for most commercial
- applications. For applications requiring a higher level of security,
- securitySize can be increased. In most applications, even at
- a very high security level, it will not be necessary to increase
- parameterSize. The security of the one-way hash functions is also
- extremely important. At this point in time, both Snefru 2.0 and MD4
- are receiving widespread scrutiny. It is too early to make reliable
- statements about the level of security that they provide.
-
- Note: Snefru 2.0 with 2 passes was broken by Eli Biham, a PhD student of
- Adi Shamir's. As a consequence, the use of 2 passes is not recommended.
- It would seem prudent to use 4 passes at the time of this writing
- (90.04.25).
-
- Further note: Although it is in general possible to prove that
- hash signatures are secure if the underlieing one-way hash function
- is secure, it should be remembered that the present program has
- some 6,000 lines of code in it. The present code has not been reviewed
- by anyone other than the author, nor does the author make any claims
- of infallibility. It is therefore probable that some security related
- bugs exist in the present implementation. This implementation should
- be verified or re-implemented independently prior to any non-experimental
- use.
-
- If anyone using this program finds a bug or error, please contact
- Ralph C.Merkle via E-mail (merkle@xerox.com) or
- via normal mail at:
-
- Xerox PARC
- 3333 Coyote Hill Road
- Palo Alto, CA 94304
- (415) 494-4000
-
-
-
- A signature (the contents of the ".sig" file) has the following
- data structure:
-
- bits description
-
- word 0
- 16 hashMethod. Uniquely identifies the particular method used.
- 4 Reserved for future use -- set to 0 for now
- 4 securitySize (in 32-bit words)
- 4 parameterSize (in 32-bit words)
- 4 bitsPerVerifier
-
- word 1
- 16 total number of words (in the signature itself) to be hashed
- 8 number of words of binary data (excluding the ascii message)
- 8 number of OTTs (One Time Trees) in this signature
-
- word 2
- 32 The date and time the message was signed, in seconds since
- 00:00:00 GMT, Jan. 1, 1970. (Standard UNIX time format).
-
- word 3
- 128 hash value of file being signed. Can occupy more than 4 words
- depending on the value of "securitySize".
-
- word 7
- 64 initial parameter. Can occupy more than 2 words depending on the
- value of "parameterSize".
-
- word 9
- 0 Additional binary data. Can occupy more than 0 words depending
- on the value in the "number of words of binary data" field
- (see above).
-
- word 9
- 0 An ascii text message to be associated with the signature. Can
- occupy more than 0 words depending on the "total number of words"
- field (see above).
-
- Further 32-bit words:
- Repeat the following block according to the
- 8-bit "number of OTTs" (One Time Trees) parameter.
-
- 64 count verifier (size varies according to "securitySize")
- 64 count verifier
- .
- . repeated 2**bitsPerVerifier times
- .
- 64 count verifier
- 32 path description (Fixed at 32 bits for all time)
- 64 branch verifier (size varies according to "securitySize")
- 64 branch verifier
- 64 branch verifier
- .
- . repeated according to "path description" (up to 31 times)
- .
- 64 branch verifier
- 64 branch verifier
-
- End of repeated block
-
-
- Both the count verifiers and the branch verifiers given above can be
- increased in size in increments of 32 bits. When securitySize is
- 2 they will be 64 bits (as shown); when securitySize is 3 they
- would be 96 bits, when securitySize is 4 they would be 128 bits,
- etc.
-
- The 64-bit initial parameter can be increased in size in increments
- of 32 bits by increasing the parameterSize from 2. Thus, when
- parameterSize is 2 the initial parameter will be 64 bits (as
- shown). When the parameterSize is 3, the initial parameter will
- be 96 bits. When the parameterSize is 4, the initial parameter
- will be 128 bits.
-
- The path description will always be 32 bits.
-
-
- Note that "word32" MUST be 32 bits
-
- Inventor, designer, and implementor: Ralph C. Merkle
-
- */
-
- #include <stdio.h>
- #include <string.h>
- #include <sys/time.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <pwd.h>
- #include "md4.h"
- #include "wellKnownDirectories"
-
- /* security related parameters */
- #define MAX_PARAMETER_SIZE 4
- #define MAX_SECURITY_SIZE 4
- #define MAX_PUBLIC_KEY_SIZE (1+MAX_PARAMETER_SIZE+MAX_SECURITY_SIZE)
- #define MACHINE_KEY_SIZE 4
- #define USER_KEY_SIZE 8
- #define USER_KEY_SIZE_IN_BYTES (4*USER_KEY_SIZE)
- #define SECRET_KEY_SIZE 4
- #define HASHED_USER_KEY_SIZE 2
- #define WORD_SIZE_IN_BITS 32
- #define MAX_HASH_VALUE_SIZE (2*MAX_SECURITY_SIZE)
-
- /*
- * Hash method constants.
- * Note: DO NOT add an additional hash method constant without
- * contacting Xerox. These constants must be globally
- * unique. Values less than 100 are reserved by Xerox and
- * should not be used by others.
- */
- #define MD4_METHOD 100
- #define SNEFRU4_METHOD 4
-
- /* other parameters and constants */
- #define MAX_OTT_SIZE 128
- #define MAX_BITS_PER_VERIFIER 10
- #define TRUE 1
- #define FALSE 0
- #define SIGN 1
- #define CHECK 0
- #define COMPUTE_OTT_HASH 2
- #define PATHOVERFLOW 1
- #define NOPATHOVERFLOW 0
- #define DO_NOT_CREATE_FILE 1
- #define CREATE_FILE 0
- #define MAX_INPUT_BLOCK_SIZE 16
- #define SNEFRU_INPUT_BLOCK_SIZE 16
- #define INPUT_BLOCK_SIZE 16
- #define MAX_SIGNATURE_SIZE 4096
- #define MAX_FILE_NAME_SIZE_IN_BYTES 100
- /* warning! If you change MAX_USER_NAME_SIZE_IN_BYTES,
- * scan through the source for all occurences --
- * some occurences are in quotes and so cannot
- * be expanded by the pre-processor (and
- * just have the integer value 300...)
- */
- #define MAX_USER_NAME_SIZE_IN_BYTES 300
- #define MAX_LINE_LENGTH 1000
- #define MAX_NO_OF_COUNTS 50
- #define MAX_STACK_DEPTH 10
- #define MAX_MESSAGE_SIZE 512
- #define MAX_MESSAGE_SIZE_IN_BYTES (MAX_MESSAGE_SIZE*4)
- #define DEBUG 0
- #define SELF_TEST 0
- #define SHOW_TIMES 0
- #define SHORT_UNUSUAL_STRING "JZ"
- #define MIN_DEPTH 1
- #define WIPE_IT 4096
- #define VERSION "@(#) Hash Signatures Version 1.0d June 19, 1990"
-
- typedef unsigned long int word32;
-
- struct SIG_BUF {
- word32 signature[MAX_SIGNATURE_SIZE];
- int locInSignature;
- };
-
- void HashExpand();
- void HashAny();
- void DoSelfTest();
- void Snefru512();
- long clock();
- long time();
- char *sprintf();
- void SnefruHashFile();
-
- #if DEBUG
- void
- PrintIt (string, val, length)
- char *string;
- word32 val[];
- int length;
- { int di;
- printf ("%s", string);
- for (di = 0; di < length; di++)
- printf (" %lx", val[di]);
- printf ("\n");
- }
- #endif
-
- /*
- * Compute the number of bits required to represent the given value
- */
- int
- SizeInBits (value)
- word32 value;
- { int i;
-
- for (i = 0; i < WORD_SIZE_IN_BITS; i++)
- if ((value >> i) == 0)
- return (i);
- return (WORD_SIZE_IN_BITS);
- }
-
-
- /*
- * Copy an input array to an output array of "size" words. The following
- * routine can be replaced with "memcpy" if this is defined in your
- * implementation.
- */
- void
- Copy (out, in, size)
- word32 in[];
- word32 out[];
- int size;
- { int i;
- for (i = 0; i < size; i++)
- out[i] = in[i];
- }
-
- /*
- * The following routine is a simple error exit routine -- it prints a
- * message and aborts
- */
- void
- ErrAbort (s)
- char *s;
- { /* Don't print empty strings */
- if (s[0] != (char) 0)
- fprintf (stderr, "%s\n", s);
- exit (2);
- };
-
- /*
- * PORTABILITY WARNING: The following routine should port
- * to most Unix systems. It returns the GMT time in seconds since
- * January 1, 1970.
- */
- long
- GetTime32()
- { time_t temp[1];
-
- (void) time(temp);
- return(temp[0]);
- }
-
- #if SHOW_TIMES
- /*
- * PORTABILITY WARNING: The following routine should port to
- * most Unix systems. It returns the cpu time in microseconds.
- */
- word32
- GetCpuTime()
- {
- return(clock());
- }
- #endif
-
-
- /* PORTABILITY WARNING: The following routine should
- * be moderately portable among Unix systems.
- * It is intended to create a new
- * file and set the access modes for that file to
- * READ and WRITE by the creator of the file, but
- * to disallow any access (read, write, or anything else)
- * by others. It returns a stream that will
- * be used to write information to the created file.
- *
- * If the file already exists, this routine will check
- * to see if it's "empty". By definition, an "empty"
- * file has 4 bytes of "0" at its beginning (or a length
- * of less than 4 bytes). If the file is empty, the stream
- * is returned. If the file is not empty, a diagnostic
- * is issued.
- *
- */
- FILE *
- OpenIfEmpty(fileName)
- char *fileName;
- { int fd;
- FILE *stream;
- char temp[4];
- int length;
- int i;
-
- fd = open(fileName, O_WRONLY | O_CREAT | O_EXCL, 0600);
- stream = fdopen(fd, "w");
- if (stream == NULL){
- stream = fopen(fileName, "r+");
- if (stream == NULL) {
- fprintf(stderr, "Can't read from \"%s\"\n", fileName);
- ErrAbort("");
- };
- length = fread(temp, 1, 4, stream);
- if (ferror(stream) != 0) {
- fprintf(stderr, "Can't access \"%s\"\n", fileName);
- ErrAbort("");
- };
- for (i=0; i<length; i++)
- if (temp[i] != 0) {
- fprintf(stderr, "File \"%s\" is not empty\n",
- fileName);
- ErrAbort(
- "To destroy old key, use \"destroyoldkey\"");
- };
- fseek(stream, 0L, 0);
- };
- return(stream);
- }
-
- /* PORTABILITY WARNING: THE FOLLOWING ROUTINE IS NOT VERY PORTABLE.
- * The following routine is intended to open and lock two files.
- * This will prevent two versions of the current program
- * from simultaneously attempting to update stable storage.
- *
- * For experimental purposes, the locking features of this routine
- * can simply be deleted (e.g., the calls to fcntl can be removed).
- * It should be remembered that THIS WILL PRODUCE A TIMING WINDOW
- * WHICH CAN RESULT IN LOSS OF SECURITY IF TWO PROGRAMS ATTEMPT
- * TO SIGN A MESSAGE WITH THE SAME SIGNING KEY AT THE SAME TIME.
- */
- void
- OpenAndLockTwoFiles(s1Ptr, s1Name, s2Ptr, s2Name)
- FILE **s1Ptr;
- char *s1Name;
- FILE **s2Ptr;
- char *s2Name;
- { int fd1;
- int fd2;
- struct flock localLock;
-
- *s1Ptr = fopen(s1Name, "r+");
- *s2Ptr = fopen(s2Name, "r+");
- if (*s1Ptr == NULL) {
- fprintf(stderr, "Can't open \"%s\"\n", s1Name);
- ErrAbort("");
- };
- if (*s2Ptr == NULL) {
- fprintf(stderr, "Can't open \"%s\"\n", s2Name);
- ErrAbort("");
- };
- fd1 = fileno(*s1Ptr);
- fd2 = fileno(*s2Ptr);
- localLock.l_type = F_WRLCK;
- localLock.l_whence = 0;
- localLock.l_start = 0;
- localLock.l_len = 0;
- /* WARNING: REMOVAL OF THE FOLLOWING TWO STATEMENTS CREATES
- * A TIMEING WINDOW. THIS SHOULD NOT MATTER FOR EXPERIMENTAL
- * USAGE, BUT SHOULD NOT BE ALLOWED IN A PRODUCTION VERSION
- */
- /*
- if (fcntl(fd1, F_SETLKW, (int) &localLock) == -1) {
- fprintf(stderr, "Can't lock \"%s\"\n", s1Name);
- ErrAbort("");
- };
- if (fcntl(fd2, F_SETLKW, (int) &localLock) == -1) {
- fprintf(stderr, "Can't lock \"%s\"\n", s2Name);
- ErrAbort("");
- };
- */
- }
-
-
- /*
- * The following routine closes a file. If there are problems
- * with the close, it prints an error message and calls ErrAbort.
- */
- void
- FileClose(stream, streamName)
- FILE *stream;
- char *streamName;
- {
- if (fclose(stream) != 0) {
- fprintf(stderr, "Can't fclose \"%s\"\n", streamName);
- ErrAbort("");
- };
- }
-
-
- /* PORTABILITY WARNING. THIS ROUTINE IS NOT VERY PORTABLE.
- * The following routine is intended to "sync" a file and then
- * close it, e.g., make sure the file is actually written to disk.
- * The routine fflush will do this on some systems (though
- * not all). The routine fsync will do this, but is
- * not widely portable. The exact method of implementing
- * FileSyncAndClose will vary from system to system.
- *
- * Note that FileSyncAndClose is presumed to "unlock" the file
- * as well as closing it.
- */
- void
- FileSyncAndClose(stream, streamName)
- FILE *stream;
- char *streamName;
- { int fd;
-
- if (fflush(stream) != 0) {
- fprintf(stderr, "Can't fflush \"%s\"\n", streamName);
- ErrAbort("");
- };
- fd = fileno(stream);
- if (fsync(fd) != 0) {
- fprintf(stderr, "Can't fsync \"%s\"\n", streamName);
- ErrAbort("");
- };
- FileClose (stream, streamName);
- }
-
- /*
- * PORTABILITY WARNING: The following routine is not very
- * portable, although it should work on most Unix systems.
- *
- * It returns 0 if the file exists, non-zero otherwise
- */
- int
- FileExists(fileName)
- char *fileName;
- { struct stat buf[1];
-
- return(stat(fileName, buf));
- }
-
- /*
- * PORTABILITY WARNING: The following routine is not very
- * portable, although it should work on most Unix systems.
- *
- * It returns 0 if the file exists and has a non-zero length
- */
- int
- SignatureExists(fileName)
- char *fileName;
- { struct stat buf[1];
-
- if (stat(fileName, buf) != 0)
- return(-1);
- if (buf->st_size == 0)
- return(-1);
- return(0);
- }
-
-
- /*
- * PORTABILITY WARNING: The following routine is not very
- * portable, although it should work on most Unix systems.
- *
- * Returns the name of the owner of the file
- */
- char *
- FileOwnersName(file)
- FILE *file;
- { struct passwd temp[1];
- struct stat buf[1];
- static char ownersName[MAX_USER_NAME_SIZE_IN_BYTES];
-
- if (fstat(fileno(file), buf) != 0)
- ErrAbort("Bad fstat");
- *temp = *getpwuid(buf->st_uid);
- strcpy(ownersName, temp->pw_name);
- return(ownersName);
- }
-
-
- /*
- * PORTABILITY WARNING: The following routine is not very
- * portable, although it should work on most Unix systems.
- *
- * The following routine is intended to determine the user's
- * name. This name will then be used as the file name which
- * holds the user's "stable storage" (machine key and stuff).
- * The most important thing to remember about this routine is
- * that THE TWO USER NAMES FOR TWO DIFFERENT USERS MUST BE
- * DIFFERENT. If not, there will be a collision in the file
- * name space, with resulting chaos and confusion.
- */
- void
- GetUserName(userName)
- char userName[MAX_USER_NAME_SIZE_IN_BYTES];
- { struct passwd temp[1];
-
- *temp = *getpwuid(getuid());
- if (strlen(temp->pw_name) >= MAX_USER_NAME_SIZE_IN_BYTES) {
- fprintf(stderr, "User name \"%s\" is too long\n",
- temp->pw_name);
- ErrAbort("");
- };
- strcpy(userName, temp->pw_name);
- }
-
- /*
- * The following routine creates the public file name which
- * holds the public file entry.
- */
- char *
- MakePublicFileName(publicDirectoryName, publicKey)
- char *publicDirectoryName;
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- { static char publicFileName[MAX_FILE_NAME_SIZE_IN_BYTES];
- char digits[50];
- /* public file entries are file names like:
- * publicDirectory/p361ad90c
- */
- strcpy(publicFileName, publicDirectoryName);
- (void) sprintf(digits, "/p%08lx_%08lx", publicKey[1], publicKey[2]);
- strcat(publicFileName, digits);
- return(publicFileName);
- };
-
-
- /*
- * The following routine creates the names of the two
- * "stable storage" files.
- */
- void
- NameStableStorage(file1Name, file2Name)
- char file1Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- char file2Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- { char userName[MAX_USER_NAME_SIZE_IN_BYTES];
-
- strcpy(file1Name, STABLE1_DIRECTORY);
- strcpy(file2Name, STABLE2_DIRECTORY);
- strcat(file1Name, "/");
- strcat(file2Name, "/");
- GetUserName(userName);
- if (5+strlen(file1Name)+strlen(userName)>=MAX_FILE_NAME_SIZE_IN_BYTES) {
- fprintf(stderr, " File name \"%s%s\" too long\n",
- file1Name, userName);
- ErrAbort("");
- };
- if (5+strlen(file2Name)+strlen(userName)>=MAX_FILE_NAME_SIZE_IN_BYTES) {
- fprintf(stderr, " File name \"%s%s\" too long\n",
- file2Name, userName);
- ErrAbort("");
- };
- strcat(file1Name, userName);
- strcat(file2Name, userName);
- strcat(file1Name, "1");
- strcat(file2Name, "2");
- }
-
- /*
- * The following routine returns the file name after removing
- * any preceding directory names. Example input:
- * "/usr/merkle/vorpal". Example output: "vorpal"
- */
- GetFileSuffix(fileName, suffixName)
- char *fileName;
- char suffixName[MAX_FILE_NAME_SIZE_IN_BYTES];
- { int i;
-
- i = strlen(fileName);
- while( (fileName[i-1]!= '/') && (i > 0)) i--;
- strcpy(suffixName, &fileName[i]);
- }
-
-
- /*
- * The following routine creates and returns the name of the
- * auxilliary information files. Note that there are two
- * auxilliary file names, only one of which is in use at any
- * given time. The one in use is selected by examining the
- * binary variable "toggle". The use of two auxilliary file
- * names (used in a "ping-pong" fashion) eliminates timing problems
- * that can arise when the signing process fails to go to
- * a normal completion.
- */
- char *
- AuxInfoFile(toggle)
- int toggle;
- { char userName[MAX_USER_NAME_SIZE_IN_BYTES];
- static char file1Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- static char file2Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- static int flag = 0;
-
- if (flag == 0) {
- flag = 1;
- strcpy(file1Name, AUX_INFO_DIRECTORY);
- strcpy(file2Name, AUX_INFO_DIRECTORY);
- strcat(file1Name, "/");
- strcat(file2Name, "/");
- GetUserName(userName);
- if (5+strlen(file1Name)+strlen(userName) >=
- MAX_FILE_NAME_SIZE_IN_BYTES) {
- fprintf(stderr, " File name \"%s%s\" too long\n",
- file1Name, userName);
- ErrAbort("");
- };
- if (5+strlen(file2Name)+strlen(userName) >=
- MAX_FILE_NAME_SIZE_IN_BYTES) {
- fprintf(stderr, " File name \"%s%s\" too long\n",
- file2Name, userName);
- ErrAbort("");
- };
- strcat(file1Name, userName);
- strcat(file2Name, userName);
- strcat(file1Name, "Aux0");
- strcat(file2Name, "Aux1");
- };
- if (toggle==0)
- return(file1Name);
- else
- return(file2Name);
- }
-
- /*
- * A simple error testing routine. If the variable is not between
- * "low" and "high" inclusive, then print out the error message and
- * abort.
- */
- void
- RangeCheck (value, low, high, message)
- int value;
- int low;
- int high;
- char *message;
- {
- if ((value < low) || (value > high)) {
- fprintf(stderr, "Error: %s is %d which is outside [%d .. %d]\n",
- message, value, low, high);
- ErrAbort ("");
- };
- }
-
- /* PORTABILITY WARNING: This routine assumes that file names
- * use "/" to separate the directory names in a full path name.
- * The following routine creates the signature file name
- * given the file name. Basically, if fileName is
- * /this/that/other/name
- * then the signature file is
- * /this/that/other/.JZname
- */
- void
- makeSigFileName(fileName, sigFileName)
- char *fileName;
- char sigFileName[MAX_FILE_NAME_SIZE_IN_BYTES];
- { int length;
- int slashLoc;
- char tempName[MAX_FILE_NAME_SIZE_IN_BYTES];
-
- length = strlen(fileName);
- if (length > MAX_FILE_NAME_SIZE_IN_BYTES-6) {
- fprintf(stderr, "Signature file name \"%s\" too long.\n",
- fileName);
- ErrAbort("");
- };
- for(slashLoc = length-1; slashLoc >= 0; slashLoc--)
- if(fileName[slashLoc] == '/') break;
- strcpy(tempName, &fileName[slashLoc+1]);
- strcpy(sigFileName, fileName);
- sigFileName[slashLoc+1] = 0; /* terminate after the '/' */
- strcat(sigFileName, ".");
- strcat(sigFileName, SHORT_UNUSUAL_STRING);
- strcat(sigFileName, tempName);
- }
-
- /**
- The following rather awful computation determines how many
- "counts" there should be. Basically, its
-
- securitySizeInBits / noOfBitsPerVerifier + fudgeFactor
-
- where your really have to add and subtract 1's to make things
- work out exactly right. The fudgeFactor provides enough
- additional counts to take care of the "check" field.
-
- Examples:
- for a 64-bit word and 4 bits per verifier, we need 18 counts
- for a 96-bit word and 4 bits per verifier, we need 27 counts
- for a 128-bit word and 4 bits per verifier, we need 35 counts
- for a 64-bit word and 8 bits per verifier, we need 10 counts
-
- */
-
- int
- ComputeNumberOfCounts (securitySize, wordSizeInBits, noOfBitsPerVerifier)
- int securitySize;
- int wordSizeInBits;
- int noOfBitsPerVerifier;
- { int noOfCounts;
- word32 maxSumOfCounts;
-
- noOfCounts = (wordSizeInBits * securitySize - 1) /
- noOfBitsPerVerifier + 1;
- /*
- * 1L << noOfBitsPerVerifier - 1 is really 2**noOfBitsPerVerifier-1
- */
- maxSumOfCounts = noOfCounts * ((1L << noOfBitsPerVerifier) - 1);
- noOfCounts += (SizeInBits (maxSumOfCounts) - 1) /
- noOfBitsPerVerifier + 1;
- return (noOfCounts);
- }
-
- /*
- * The following routine unpacks the 32-bits in "header" and
- * puts them into the specified output variables.
- * Yes, it would be nice to use bit fields, but there's
- * no guarantee that the bit fields would be laid out in
- * the same fashion from compiler to compiler.
- */
- void
- UnPackHeader ( header, hashMethodPtr,
- securitySizePtr, parameterSizePtr,
- noOfBitsPerVerifierPtr, hashValueSizePtr, noOfCountsPtr)
- word32 header;
- int *hashMethodPtr;
- int *securitySizePtr;
- int *parameterSizePtr;
- int *noOfBitsPerVerifierPtr;
- int *hashValueSizePtr;
- int *noOfCountsPtr;
- { *hashMethodPtr = header >> 16;
- *securitySizePtr = header>>8 & 0xf;
- *parameterSizePtr = header>>4 & 0xf;
- *noOfBitsPerVerifierPtr = header & 0xf;
- *hashValueSizePtr = 2 * (*securitySizePtr);
- *noOfCountsPtr = ComputeNumberOfCounts (*securitySizePtr,
- WORD_SIZE_IN_BITS, *noOfBitsPerVerifierPtr);
- /* range check everything */
- switch (*hashMethodPtr) {
- case SNEFRU4_METHOD: break;
- case MD4_METHOD: break;
- default:
- ErrAbort("bad hash method");
- };
- RangeCheck (*securitySizePtr, 2, MAX_SECURITY_SIZE,
- "security size");
- RangeCheck (*parameterSizePtr, 2, MAX_PARAMETER_SIZE,
- "parameter size");
- RangeCheck (*noOfBitsPerVerifierPtr, 0, MAX_BITS_PER_VERIFIER,
- "bits per verifier");
- RangeCheck (*noOfCountsPtr, 0, MAX_NO_OF_COUNTS,
- "number of counts");
- }
-
- /*
- * The following routine packs the given input variables
- * into *headerPtr.
- */
- void
- PackHeader ( headerPtr, hashMethod,
- securitySize, parameterSize, noOfBitsPerVerifier)
- word32 *headerPtr;
- int hashMethod;
- int securitySize;
- int parameterSize;
- int noOfBitsPerVerifier;
- { switch (hashMethod) {
- case SNEFRU4_METHOD: break;
- case MD4_METHOD: break;
- default:
- ErrAbort("bad hash method");
- };
- RangeCheck (securitySize, 2, MAX_SECURITY_SIZE,
- "security size");
- RangeCheck (parameterSize, 2, MAX_PARAMETER_SIZE,
- "parameter size");
- RangeCheck (noOfBitsPerVerifier, 0, MAX_BITS_PER_VERIFIER,
- "number of bits per verifier");
-
- *headerPtr = hashMethod << 16 |
- securitySize << 8 |
- parameterSize << 4 |
- noOfBitsPerVerifier;
- }
-
- /*
- * The following routine computes the size of the public key
- * by examining the first word (32 bits) of the public key.
- * The first word encodes the hash method, the security size,
- * the parameter size, and other good stuff. The size of
- * the public key in 32-bit words is just the security size
- * plus the parameter size plus 1 (the size of the 32-bit header).
- */
- int
- ComputePublicKeySize(publicKey)
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- { int hashMethod;
- int securitySize;
- int parameterSize;
- int noOfBitsPerVerifier;
- int hashValueSize;
- int noOfCounts;
-
- if (publicKey[0] == 0) return(1);
- UnPackHeader ( publicKey[0], &hashMethod,
- &securitySize, ¶meterSize,
- &noOfBitsPerVerifier, &hashValueSize, &noOfCounts);
- return(securitySize + parameterSize + 1);
- }
-
- /*
- * The following routine converts an array of word32 to a byte
- * array. It is primarily intended to eliminate the byte-ordering problem.
- * VAXes order the bytes in a character array differently than SUN's do. This
- * routine is not needed on SUNs (or other big-endian machines).
- */
- void
- ConvertLongToChar (wordBuffer, charBuffer, wordLength)
- word32 wordBuffer[ /* wordLength */ ];/* input buffer */
- char charBuffer[ /* wordLength * 4 */ ]; /* output buffer */
- int wordLength;
- { int i;
- word32 temp;
-
- for (i = 0; i < wordLength; i++) {
- temp = wordBuffer[i];
- charBuffer[4 * i + 0] = (temp >> 24) & 0xff;
- charBuffer[4 * i + 1] = (temp >> 16) & 0xff;
- charBuffer[4 * i + 2] = (temp >> 8) & 0xff;
- charBuffer[4 * i + 3] = (temp >> 0) & 0xff;
- };
- };
-
- /*
- * The following routine converts a byte array to an array of word32.
- * It is intended to eliminate the byte-ordering problem. VAXes order
- * the bytes in a character array differently than SUN's do. Some
- * machines do REALLY horrible things -- this routine should be
- * fairly portable (if not blindingly fast).
- */
- void
- ConvertBytes (buffer, wordBuffer, charCount)
- char buffer[]; /* input buffer */
- word32 wordBuffer[]; /* output buffer */
- int charCount;
- { int i;
- int wordCount;
- word32 t0, t1, t2, t3;
-
- if (charCount <= 0) return;
- wordCount = (charCount+3)>>2;
- for (i = 0; i < wordCount; i++) {
- t0 = buffer[4 * i + 0];
- t1 = buffer[4 * i + 1];
- t2 = buffer[4 * i + 2];
- t3 = buffer[4 * i + 3];
- t0 &= 0xff;
- t1 &= 0xff;
- t2 &= 0xff;
- t3 &= 0xff;
- wordBuffer[i] = (t0 << 24) | (t1 << 16) | (t2 << 8) | t3;
- };
- /* Get rid of the bottom bytes that aren't wanted */
- switch (charCount&3) {
- case 0:
- break;
- case 1: wordBuffer[wordCount-1] &= 0xff000000L;
- break;
- case 2: wordBuffer[wordCount-1] &= 0xffff0000L;
- break;
- case 3: wordBuffer[wordCount-1] &= 0xffffff00L;
- };
- }
-
-
- /*
- * The following routine increments a "path" location. This is a 32-bit word
- * that encodes a "path" up a binary tree. Succeeding bits in the word
- * following the first "1" bit show whether to take the left or right branch
- * in the "path" from the root of the tree to any particular leaf.
- *
- * When a "path" is incremented, it might overflow. In this case, the path value
- * is left unchanged and the routine returns the value "PATHOVERFLOW".
- * Otherwise the routine returns the value "NOPATHOVERFLOW"
- */
- int
- BumpPath (pathPtr)
- word32 *pathPtr;
- { /*
- * If the bit pattern passed in looks like: 000...00111....11
- *
- * then, by definition, incrementing this by one causes an "overflow" in
- * the path description because it alters the location of the leading
- * "1" bit.
- */
- if (SizeInBits (*pathPtr) != SizeInBits ((*pathPtr) + 1))
- return (PATHOVERFLOW);
- else {
- (*pathPtr)++;
- return (NOPATHOVERFLOW);
- };
- }
-
- /*
- * The following routine sets a single bit in "array" at the designated
- * offset
- */
- void
- SetBit (array, bitOffset)
- word32 array[];
- int bitOffset;
- { int wordOffset;
- word32 mask;
-
- /*
- * compute word and bit offsets for a 32-bit word size. Yes,
- * "WORD_SIZE_IN_BITS" is 32. If not, watch out!
- */
- wordOffset = bitOffset / WORD_SIZE_IN_BITS;
- bitOffset %= WORD_SIZE_IN_BITS;
- mask = 1;
- mask <<= 31; /* Set the topmost bit */
- /* set the bit... */
- array[wordOffset] |= (mask >> bitOffset);
- }
-
- /*
- * The following routine grabs noOfBits bits from the given array, at the
- * given bitOffset. Bits beyond the array limit are defined to be 0
- */
- word32
- FetchBits (array, arrayLimit, bitOffset, noOfBits)
- word32 array[ /* arrayLimit */ ];
- int arrayLimit;
- int bitOffset;
- int noOfBits;
- { word32 temp1, temp2;
- int wordOffset;
-
- /*
- * compute word and bit offsets for a 32-bit word size. Yes,
- * "WORD_SIZE_IN_BITS" is 32. If not, watch out!
- */
- wordOffset = bitOffset / WORD_SIZE_IN_BITS;
- bitOffset %= WORD_SIZE_IN_BITS;
-
-
- /* fetch the (possibly two) words involved from the array */
- temp1 = array[wordOffset];
- temp2 = array[wordOffset + 1];
-
- /* check for references beyond array limit */
- if (wordOffset >= arrayLimit)
- temp1 = 0;
- if (wordOffset + 1 >= arrayLimit)
- temp2 = 0;
-
-
- /* grab the bits from the right spot */
- temp1 <<= bitOffset;
- temp1 >>= WORD_SIZE_IN_BITS - noOfBits;
-
- /* "or" in the bits from the second word, if needed */
- if (bitOffset + noOfBits > WORD_SIZE_IN_BITS)
- temp1 |= (temp2 >> (2 * WORD_SIZE_IN_BITS
- - bitOffset - noOfBits));
- /* and return the bits! */
- return (temp1);
- }
-
- void
- DownLeft (p, n)
- word32 p[ /* n */ ];
- int n;
- { word32 overFlow;
- int temp;
- int i;
-
- /* grab the bottom bit from the last word of the parameter */
- overFlow = p[n - 1] & 1;
- for (i = 0; i < n; i++) {
- temp = p[i] & 1;
- p[i] >>= 1;
- p[i] |= overFlow << 31;
- overFlow = temp;
- };
- };
-
- void
- DownRight (p, n)
- word32 p[ /* n */ ];
- { word32 overFlow;
- int temp;
- int i;
-
- /* grab the bottom bit from the last word of the parameter */
- overFlow = p[n - 1] & 1;
- overFlow ^= 1; /* flip the bit */
- for (i = 0; i < n; i++) {
- temp = p[i] & 1;
- p[i] >>= 1;
- p[i] |= overFlow << 31;
- overFlow = temp;
- };
- };
-
- void
- UpRight (p, n)
- word32 p[ /* n */ ];
- int n;
- { word32 overFlow;
- int temp;
- int i;
-
- /* Quick test for overflow */
- if ((p[0] >> (WORD_SIZE_IN_BITS - 4)) == 0)
- ErrAbort (" Parameter overflow!");
-
- /* grab the top bit from the first word of the parameter */
- overFlow = (p[0] >> 31) & 1;
- for (i = n - 1; i >= 0; i--) {
- temp = (p[i] >> 31) & 1;
- p[i] <<= 1;
- p[i] |= overFlow;
- overFlow = temp;
- };
- };
-
- void
- UpLeft (p, n)
- word32 p[ /* n */ ];
- int n; /* length (in 32-bit words) of the parameter */
- { word32 overFlow;
- int temp;
- int i;
-
- /* Quick test for overflow */
- if ((p[0] >> (WORD_SIZE_IN_BITS - 4)) == 0)
- ErrAbort (" Parameter overflow!");
-
- /* grab the top bit from the first word of the parameter */
- overFlow = (p[0] >> 31) & 1;
- for (i = n - 1; i >= 0; i--) {
- temp = (p[i] >> 31) & 1;
- p[i] <<= 1;
- p[i] |= overFlow;
- overFlow = temp;
- };
- p[n - 1] ^= 1; /* flip the bottom bit */
- };
-
-
- void
- UpPath (p, parameterSize, path)
- word32 p[];
- word32 path;
- { word32 reversePath;
- int pathLength;
-
- if (path <= 0)
- ErrAbort ("logic error -- path is not positive");
- reversePath = 0;
- /* First, reverse the bits */
- for (pathLength = -1; path != 0; pathLength++) {
- reversePath <<= 1;
- reversePath |= (path & 1);
- path >>= 1;
- };
- /*
- * and throw away the bottom bit (which used to be the top bit)
- * because it's always 1
- */
- reversePath >>= 1;
-
- /* and go up the path! */
-
- while (pathLength != 0) {
- if ((reversePath & 1) == 1)
- UpLeft (p, parameterSize);
- else
- UpRight (p, parameterSize);
- reversePath >>= 1;
- pathLength--;
- };
- }
-
- void
- HashThreeItems (out, outSize,
- in1, in1Size,
- in2, in2Size,
- in3, in3Size,
- hashMethod)
- word32 out[];
- int outSize;
- word32 in1[];
- int in1Size;
- word32 in2[];
- int in2Size;
- word32 in3[];
- int in3Size;
- int hashMethod;
- { word32 tempIn[MAX_INPUT_BLOCK_SIZE];
-
- if ((in1Size + in2Size + in3Size) > MAX_INPUT_BLOCK_SIZE)
- ErrAbort ("Input to HashThreeItems is too large");
- Copy (&tempIn[0], in1, in1Size);
- Copy (&tempIn[in1Size], in2, in2Size);
- Copy (&tempIn[in1Size + in2Size], in3, in3Size);
- HashAny (out, outSize, tempIn, in1Size + in2Size + in3Size, hashMethod);
- };
-
- void
- HashTwoItems (out, outSize,
- in1, in1Size,
- in2, in2Size,
- hashMethod)
- word32 out[];
- int outSize;
- word32 in1[];
- int in1Size;
- word32 in2[];
- int in2Size;
- int hashMethod;
- { word32 empty[1];
- HashThreeItems (out, outSize, in1, in1Size,
- in2, in2Size, empty, 0, hashMethod);
- };
-
- /*
- * This routine increments a 64-bit counter by the given increment.
- */
- void
- Increment64BitCounter (counter, increment)
- word32 counter[2];
- long int increment;
- { word32 maxInt = 0xffffffffL;
-
- if ( (maxInt-counter[1]) < increment) {
- /* Overflow from the lower 32 bits */
- if (counter[0] == maxInt)
- ErrAbort("64-bit counter overflowed");
- /* bump the upper 32 bits */
- counter[0]++;
- /* and then increment the lower 32 bits */
- /* without ever overflowing! */
- counter[1] = maxInt-counter[1];
- counter[1] = increment - counter[1];
- }
- else
- /* increment the total number of bits read */
- counter[1] += increment;
- }
-
- /* Read a "chunk" of "chunkSize" 32-bit words from the given
- * file. Take care of any byte ordering problems involved
- * in reading the 32-bit input values
- */
- int
- ReadChunk (file, chunk, chunkSize)
- FILE *file;
- word32 *chunk;
- int chunkSize;
- { char temp[4*SNEFRU_INPUT_BLOCK_SIZE];
- int byteCount;
- int i;
-
- byteCount = fread(temp, 1, 4*chunkSize, file);
- if (ferror(file) != 0) {
- ErrAbort("Can't read from input file");
- };
- for (i=byteCount; i<4*chunkSize; i++)
- temp[i] = 0;
- ConvertBytes(temp, chunk, 4*chunkSize);
- return(byteCount);
- }
-
- void
- HashFile (inputFile, hashValue, hashValueSize, hashMethod)
- FILE *inputFile;
- word32 hashValue[MAX_HASH_VALUE_SIZE];
- int hashValueSize;
- { switch (hashMethod) {
- case MD4_METHOD:
- Md4HashFile(inputFile, hashValue, hashValueSize);
- break;
- case SNEFRU4_METHOD:
- SnefruHashFile(inputFile, hashValue, hashValueSize, hashMethod);
- break;
- };
- }
-
- /* Write out information to one of the "stable storage files".
- * The stable storage file is small, so it's kept in readable
- * ASCII format. This simplifies debugging, but makes the
- * following routine more complex than a simple series of binary
- * writes.
- */
- void
- WriteOneStableStorageFile (stableStorageFile,
- machineKey,
- publicKey,
- hashedUserKey,
- path,
- top,
- toggle,
- OTTsize)
- FILE *stableStorageFile;
- word32 machineKey[MACHINE_KEY_SIZE];
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 hashedUserKey[HASHED_USER_KEY_SIZE];
- word32 path[MAX_STACK_DEPTH];
- int top;
- int toggle;
- { int i;
- int printCode;
- int publicKeySize;
-
- RangeCheck(top, 0, MAX_STACK_DEPTH-1, "top");
- rewind(stableStorageFile);
- printCode = fprintf (stableStorageFile, "machineKey =");
- if (printCode <= 0)
- ErrAbort ("can't write string to stable storage");
- for (i = 0; i < MACHINE_KEY_SIZE; i++) {
- printCode = fprintf (stableStorageFile,
- " %08lx", machineKey[i]);
- if (printCode <= 0)
- ErrAbort ("bad write to stable storage");
- if ((i % 4 == 3) && (MACHINE_KEY_SIZE - 1 > i))
- printCode = fprintf (stableStorageFile,
- "\n ");
- if (printCode <= 0)
- ErrAbort ("bad string write to stable storage");
- };
- printCode = fprintf (stableStorageFile, "\nhashedUserKey =");
- if (printCode <= 0)
- ErrAbort ("can't write string to stable storage");
- for (i = 0; i < HASHED_USER_KEY_SIZE; i++) {
- printCode = fprintf (stableStorageFile,
- " %08lx", hashedUserKey[i]);
- if (printCode <= 0)
- ErrAbort ("bad write to stable storage");
- if ((i % 4 == 3) && (HASHED_USER_KEY_SIZE - 1 > i))
- printCode = fprintf (stableStorageFile,
- "\n ");
- if (printCode <= 0)
- ErrAbort ("bad string write to stable storage");
- };
- printCode = fprintf (stableStorageFile, "\npublicKey =");
- if (printCode <= 0)
- ErrAbort ("can't write string to stable storage");
- publicKeySize = ComputePublicKeySize(publicKey);
- for (i = 0; i < publicKeySize; i++) {
- printCode = fprintf (stableStorageFile,
- " %08lx", publicKey[i]);
- if (printCode <= 0)
- ErrAbort ("bad public key write to stable storage");
- };
- printCode = fprintf (stableStorageFile, "\ntoggle = %d\n", toggle);
- if (printCode <= 0)
- ErrAbort ("can't write toggle to stable storage");
- printCode = fprintf (stableStorageFile, "OTTsize = %d\n", OTTsize);
- if (printCode <= 0)
- ErrAbort ("can't write OTTsize to stable storage");
- printCode = fprintf (stableStorageFile, "top = %d\npath =", top);
- if (printCode <= 0)
- ErrAbort ("can't write top to stable storage");
- for (i = 0; i <= top; i++) {
- printCode = fprintf (stableStorageFile, " %lx", path[i]);
- if (printCode <= 0)
- ErrAbort ("bad path write to stable storage");
- };
- printCode = fprintf (stableStorageFile, "\n");
- if (printCode <= 0)
- ErrAbort ("can't write string to stable storage");
- }
-
- /*
- * The following routines write the machine key, the current signature
- * path, and the public key redundantly to two "stable storage" files.
- * Stable storage can be relied upon not to be changed, altered, or lost in
- * some unpleasant fashion. As long as one of the two "stable storage" files
- * is intact, the aux info can be recovered. If the two stable storage
- * files are kept on separate disk drives, then it is unlikely that both will
- * be lost simultaneously.
- *
- * Stable storage should not be backed up. If one stable storage file is
- * damaged, it should be re-initialized from the other stable storage file.
- * In the unlikely event that both stable storage files are damaged, then no
- * further messages should be signed with this signing key. A new
- * signing-key/checking-key pair should be issued.
- *
- * Note that loss of the secret signing key is a nuisance, but does not cause
- * any significant damage. Messages already signed continue to be valid even
- * if the secret signing key is destroyed. The newly issued secret signing
- * key can be used to sign new messages. If interruption of service is an
- * issue, then a second signing-key/checking-key pair can be prepared for the
- * user in advance of need. This will also provide uninterrupted service to
- * the user in case of other problems, such as theft, destruction, loss,
- * compromise, etc.
- *
- * (Note that these precautions will prevent compromise of the machine
- * key and will also prevent re-signing of two different messages with
- * the same one-time signature. The latter would seriously
- * jeopordize security).
- *
- * Exactly how this "stable storage" is realized in any particular instance is
- * left up to the implementor.
- */
- void
- WriteStableStorage (file1, file2, machineKey,
- publicKey, hashedUserKey,
- path, top, toggle, OTTsize)
- FILE *file1;
- FILE *file2;
- word32 machineKey[];
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 hashedUserKey[HASHED_USER_KEY_SIZE];
- word32 path[];
- int top;
- int toggle;
- int OTTsize;
- { WriteOneStableStorageFile (file1, machineKey, publicKey,
- hashedUserKey, path,
- top, toggle, OTTsize);
- WriteOneStableStorageFile (file2, machineKey, publicKey,
- hashedUserKey, path,
- top, toggle, OTTsize);
- };
-
-
- /*
- * The following two routines read the machine key and the path from stable
- * storage. If the two files have conflicting values, abort at once.
- */
- void
- ReadOneStableStorageFile (stableStorageFile, machineKey, publicKey,
- hashedUserKey, path,
- topPtr, togglePtr, OTTsizePtr)
- FILE *stableStorageFile;
- word32 machineKey[MACHINE_KEY_SIZE];
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 hashedUserKey[HASHED_USER_KEY_SIZE];
- word32 path[MAX_STACK_DEPTH];
- int *topPtr;
- int *togglePtr;
- int *OTTsizePtr;
- { int i;
- int scanCode;
- int publicKeySize;
-
- scanCode = fscanf (stableStorageFile, " machineKey = ");
- if (scanCode != 0)
- ErrAbort ("can't read string from stable storage");
- for (i = 0; i < MACHINE_KEY_SIZE; i++) {
- scanCode = fscanf (stableStorageFile, " %lx", &machineKey[i]);
- if (scanCode != 1)
- ErrAbort ("bad read from stable storage");
- };
- scanCode = fscanf (stableStorageFile, " hashedUserKey = ");
- if (scanCode != 0)
- ErrAbort ("can't read string from stable storage");
- for (i = 0; i < HASHED_USER_KEY_SIZE; i++) {
- scanCode = fscanf (stableStorageFile,
- " %lx", &hashedUserKey[i]);
- if (scanCode != 1)
- ErrAbort ("bad read from stable storage");
- };
-
- /* Read in the public key */
- scanCode = fscanf (stableStorageFile, " publicKey = ");
- if (scanCode != 0)
- ErrAbort ("can't read string from stable storage");
- scanCode = fscanf (stableStorageFile, " %lx", publicKey);
- if (scanCode != 1)
- ErrAbort ("bad path read from stable storage");
- publicKeySize = ComputePublicKeySize(publicKey);
- for (i = 1; i < publicKeySize; i++) {
- scanCode = fscanf (stableStorageFile, " %lx", &publicKey[i]);
- if (scanCode != 1)
- ErrAbort ("bad path read from stable storage");
- };
-
- scanCode = fscanf (stableStorageFile,
- " toggle = %d", togglePtr);
- if (scanCode != 1)
- ErrAbort (
- "can't read toggle from stable storage");
- RangeCheck(*togglePtr, 0, 1, "toggle");
-
- scanCode = fscanf (stableStorageFile,
- " OTTsize = %d", OTTsizePtr);
- if (scanCode != 1)
- ErrAbort (
- "can't read OTTsize from stable storage");
- RangeCheck(*OTTsizePtr, 4, MAX_OTT_SIZE, "OTT size");
-
- scanCode = fscanf (stableStorageFile, " top = %d path =", topPtr);
- if (scanCode != 1)
- ErrAbort ("can't read top from stable storage");
-
- if ((*topPtr) < 0)
- ErrAbort ("top negative");
- if ((*topPtr) >= MAX_STACK_DEPTH)
- ErrAbort ("top too big");
-
- for (i = 0; i <= *topPtr; i++) {
- scanCode = fscanf (stableStorageFile, " %lx", &path[i]);
- if (scanCode != 1)
- ErrAbort ("bad path read from stable storage");
- };
- }
-
-
- /*
- * Read the inputs from both files, and then compare them. If they disagree,
- * abort.
- */
- void
- ReadStableStorage (file1, file2, machineKey, publicKey,
- hashedUserKey, path, topPtr, togglePtr, OTTsizePtr)
- FILE *file1;
- FILE *file2;
- word32 machineKey[MACHINE_KEY_SIZE];
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 hashedUserKey[HASHED_USER_KEY_SIZE];
- word32 path[MAX_STACK_DEPTH];
- int *topPtr;
- int *togglePtr;
- int *OTTsizePtr;
- { word32 tempMachineKey[MACHINE_KEY_SIZE];
- word32 tempPublicKey[MAX_PUBLIC_KEY_SIZE];
- word32 tempPath[MAX_STACK_DEPTH];
- int publicKeySize;
- int tempTop;
- int tempToggle;
- int tempOTTsize;
- int i;
-
- ReadOneStableStorageFile (file1, machineKey, publicKey,
- hashedUserKey, path, topPtr, togglePtr, OTTsizePtr);
-
- ReadOneStableStorageFile (file2, tempMachineKey, tempPublicKey,
- hashedUserKey, tempPath, &tempTop, &tempToggle, &tempOTTsize);
-
- if (tempTop != (*topPtr))
- ErrAbort ("conflicting stable storage for \"top\"");
-
- if (tempToggle != (*togglePtr))
- ErrAbort ("conflicting stable storage for \"toggle\"");
-
- if (tempOTTsize!= (*OTTsizePtr))
- ErrAbort ("conflicting stable storage for \"OTTsize\"");
-
- for (i = 0; i < tempTop; i++)
- if (tempPath[i] != path[i])
- ErrAbort ("conflicting stable storage for path");
-
- for (i = 0; i < MACHINE_KEY_SIZE; i++)
- if (machineKey[i] != tempMachineKey[i])
- ErrAbort ("conflicting stable storage for machine key");
-
- publicKeySize = ComputePublicKeySize(publicKey);
- for (i = 0; i < publicKeySize; i++)
- if (publicKey[i] != tempPublicKey[i])
- ErrAbort ("conflicting stable storage for public key");
- }
-
- /*
- * This routine reads in the data from the "signature file", which
- * is kept in binary, and then unpacks the data, performs range
- * checking, and generally converts the information in the file
- * into a useful internal format.
- */
- void
- ReadSignature (sigFile, hashMethodPtr,
- noOfSignaturesPtr, parameter, parameterSizePtr,
- securitySizePtr, hashValueSizePtr, noOfCountsPtr,
- noOfBitsPerVerifierPtr, signature, locationPtr,
- wordsToHashPtr, hashFromSig, dateTime32Ptr,
- binaryDataFieldSizePtr, message)
- FILE *sigFile;
- int *hashMethodPtr;
- int *noOfSignaturesPtr;
- word32 parameter[MAX_PARAMETER_SIZE];
- int *parameterSizePtr;
- int *securitySizePtr;
- int *hashValueSizePtr;
- int *noOfCountsPtr;
- int *noOfBitsPerVerifierPtr;
- word32 signature[MAX_SIGNATURE_SIZE];
- int *locationPtr;
- word32 *wordsToHashPtr;
- word32 hashFromSig[MAX_HASH_VALUE_SIZE];
- word32 *dateTime32Ptr;
- int *binaryDataFieldSizePtr;
- char message[MAX_MESSAGE_SIZE_IN_BYTES];
- { int lengthOfSignatureInBytes;
- word32 *locInSig;
- int messageSize;
- char charSignature[MAX_SIGNATURE_SIZE * 4];
-
- lengthOfSignatureInBytes =
- fread (charSignature, 1,
- MAX_SIGNATURE_SIZE * 4, sigFile);
- if (ferror(sigFile) != 0) {
- ErrAbort("Can't read from signature file");
- };
- ConvertBytes (charSignature, signature, MAX_SIGNATURE_SIZE * 4);
- /* dull error tests */
- RangeCheck (lengthOfSignatureInBytes, 0, MAX_SIGNATURE_SIZE * 4,
- "Signature length");
- if (lengthOfSignatureInBytes % 4 != 0)
- ErrAbort ("Signature length not a multiple of 4 bytes");
- /* We've read in the signature file */
-
- locInSig = signature;
- /* now unpack the header */
- UnPackHeader( *locInSig, hashMethodPtr,
- securitySizePtr, parameterSizePtr,
- noOfBitsPerVerifierPtr, hashValueSizePtr, noOfCountsPtr);
- /*
- * return the number of one-time signatures used in this
- * signature
- */
- locInSig++;
- *noOfSignaturesPtr = (*locInSig) & 0xff;
- *wordsToHashPtr = (*locInSig)>>16;
- *binaryDataFieldSizePtr = ((*locInSig)>>8) & 0xff;
- locInSig++;
- *dateTime32Ptr = *locInSig;
- locInSig++;
- Copy(hashFromSig, locInSig, *hashValueSizePtr);
- locInSig += *hashValueSizePtr;
- Copy(parameter, locInSig, *parameterSizePtr);
- locInSig += *parameterSizePtr;
- messageSize = *wordsToHashPtr - *binaryDataFieldSizePtr-2;
- if (messageSize >= MAX_MESSAGE_SIZE-4)
- ErrAbort("Message size in signature is too big");
- ConvertLongToChar(locInSig, message, messageSize);
- locInSig += messageSize;
-
- /*
- * compute the start of the one-time signature count verifier block
- */
- *locationPtr = locInSig - signature;
- };
-
-
- /*
- * This routine checks an authentication path
- */
- void
- CheckAuthenticationPath (checkValue, securitySize,
- parameter, parameterSize,
- hashMethod,
- path,
- branchVerifiers)
- word32 checkValue[];
- int securitySize;
- word32 parameter[];
- int parameterSize;
- int hashMethod;
- word32 path;
- word32 branchVerifiers[];
- { while (path != 1) {
- if ((path & 1) == 1) {
-
- #if DEBUG
- printf ("auth path bit is 1\n");
- PrintIt ("branchVerifiers=",
- branchVerifiers, securitySize);
- PrintIt ("checkValue=", checkValue, securitySize);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
- DownRight (parameter, parameterSize);
-
- HashThreeItems (checkValue, securitySize,
- branchVerifiers, securitySize,
- checkValue, securitySize,
- parameter, parameterSize,
- hashMethod);
- } else {
-
- #if DEBUG
- printf ("auth path bit is 0\n");
- PrintIt ("checkValue=", checkValue, securitySize);
- PrintIt ("branchVerifiers=",
- branchVerifiers, securitySize);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
-
- DownLeft (parameter, parameterSize);
-
- HashThreeItems (checkValue, securitySize,
- checkValue, securitySize,
- branchVerifiers, securitySize,
- parameter, parameterSize,
- hashMethod);
- };
- path >>= 1;
- branchVerifiers += securitySize;
- };
- }
-
- /*
- * The following routine accepts as input a public key
- * (normally computed from a signature) and the name
- * of the public directory. It looks up the public key
- * in the public directory. It returns 0 if the search was
- * successful, and sets "userName" to the name found in
- * the public directory. It returns non-zero if the search
- * was unsuccessful, and userName is left empty ("");
- *
- */
- int
- LookUpPublicKey (publicKey, publicDirectoryName, userName)
- word32 publicKey[MAX_PUBLIC_KEY_SIZE]; /* Input */
- char *publicDirectoryName;
- char userName[MAX_USER_NAME_SIZE_IN_BYTES]; /* Output */
- { char *publicFileName;
- word32 tempPublicKey[MAX_PUBLIC_KEY_SIZE];
- int i;
- int publicKeySize;
- int scanReturnCode;
- FILE *publicFile;
- char *ownersName;
- char tempName[MAX_USER_NAME_SIZE_IN_BYTES];
-
- /* open the "public file" entry, read only */
- publicFileName = MakePublicFileName(publicDirectoryName, publicKey);
- publicFile = fopen (publicFileName, "r");
- userName[0] = (char) 0;
- if (publicFile == NULL)
- return(-1);
- ownersName = FileOwnersName(publicFile);
- scanReturnCode = fscanf (publicFile,
- /* WARNING:
- * update constant 300 when MAX_USER_NAME_SIZE_IN_BYTES
- * is changed. Updating this constant is required
- * to correctly prevent overflow if the public file
- * has a name which is too long.
- */
- "%*[^#]#%300[^:]: %lx", tempName, tempPublicKey);
- if (scanReturnCode == 0) /* didn't scan the "#" */
- scanReturnCode = fscanf (publicFile,
- /* WARNING: update constant 300 when
- * MAX_USER_NAME_SIZE_IN_BYTES is changed.
- */
- "#%300[^:]: %lx", tempName, tempPublicKey);
- if(scanReturnCode != 2)
- return(-1);
- publicKeySize = ComputePublicKeySize(tempPublicKey);
- for (i = 1; i < publicKeySize; i++) {
- scanReturnCode = fscanf(publicFile, " %lx", &tempPublicKey[i]);
- if (scanReturnCode != 1)
- return(-1);
- };
- for (i = 0; i < publicKeySize; i++)
- if (publicKey[i] != tempPublicKey[i])
- return(-1);
- FileClose (publicFile, publicFileName);
- if ( (strlen(tempName)+strlen(ownersName)) >=
- MAX_USER_NAME_SIZE_IN_BYTES-4) {
- fprintf(stderr,
- "String: \"%s:%s\" is longer than %d bytes\n",
- ownersName, tempName, MAX_USER_NAME_SIZE_IN_BYTES);
- ErrAbort("");
- };
- strcpy(userName, ownersName);
- strcat(userName, ":");
- strcat(userName, tempName);
- return(0);
- }
-
- /*
- * The following routine creates/checks a one-time signature. Note
- * that the routine can be used both to sign and to check a value to
- * sign.
- */
- void
- OneTimeSignCheck (valueToSign, securitySize,
- parameter, parameterSize, signCheckFlag,
- noOfCounts, bitsPerVerifier,
- xyVector, hashForSignature, hashMethod)
- word32 valueToSign[ /* securitySize */ ];
- int securitySize;
- word32 parameter[MAX_PARAMETER_SIZE];
- int parameterSize;
- int signCheckFlag;
- word32 xyVector[MAX_NO_OF_COUNTS * MAX_SECURITY_SIZE];
- word32 hashForSignature[MAX_SECURITY_SIZE];
- int hashMethod;
- { int i, j;
- word32 count[MAX_NO_OF_COUNTS];
- word32 twoToTheBitsPerVerifier;
- int bitsNeeded;
- word32 parameterLastWord;
-
- twoToTheBitsPerVerifier = 1;
- twoToTheBitsPerVerifier <<= bitsPerVerifier;
-
-
- /* compute the counts */
- for (i = 0; i < noOfCounts; i++)
- count[i] =
- FetchBits (valueToSign, securitySize,
- i * bitsPerVerifier, bitsPerVerifier);
-
-
- /*
- * The valueToSign has some bits in it. We need to pad out this
- * array with a "check" field to prevent tampering with the count
- * values by evil doers
- */
- FillInTheCheckField (count, noOfCounts,
- securitySize, bitsPerVerifier,
- twoToTheBitsPerVerifier);
- /*
- * Okay, we've spread the bits out into the array "count" and also
- * added the check field to the array "count"
- */
- /*
- * Compute the number of bits needed for the combined count and the
- * bitsPerVerifier in the parameter
- */
- bitsNeeded = SizeInBits ((word32)
- noOfCounts * twoToTheBitsPerVerifier);
- /* and make room for that many bits in the parameter */
- for (i = 0; i < bitsNeeded; i++)
- UpRight (parameter, parameterSize);
- /*
- * Remember the last word of the parameter -- we diddle it to
- * parameterize each application of hashN for the one time signature
- */
- parameterLastWord = parameter[parameterSize - 1];
- /* zero out the hash value result for the signature */
- for (i = 0; i < securitySize; i++)
- hashForSignature[i] = 0;
- for (i = 0; i < noOfCounts; i++) {
- /* hash down the sequence */
- if (signCheckFlag != CHECK) {
- for (j = twoToTheBitsPerVerifier - 1;
- j > count[i]; j--) {
- /* tweak the parameter for <i,j> */
- parameter[parameterSize - 1] =
- parameterLastWord ^
- (i * twoToTheBitsPerVerifier + j);
- HashTwoItems (&xyVector[i * securitySize],
- securitySize,
- &xyVector[i * securitySize],
- securitySize,
- parameter, parameterSize,
- hashMethod);
- };
- };
- /* keep hashing */
- if (signCheckFlag != SIGN) {
- for (j = count[i]; j > 0; j--) {
- parameter[parameterSize - 1] =
- parameterLastWord ^
- (i * twoToTheBitsPerVerifier + j);
- HashTwoItems (&xyVector[i * securitySize],
- securitySize,
- &xyVector[i * securitySize],
- securitySize,
- parameter, parameterSize,
- hashMethod);
- };
- parameter[parameterSize - 1] = parameterLastWord ^
- (i * twoToTheBitsPerVerifier);
- HashThreeItems (hashForSignature, securitySize,
- hashForSignature, securitySize,
- &xyVector[i * securitySize],
- securitySize,
- parameter, parameterSize,
- hashMethod);
- };
- };
- /* Restore the parameter */
- parameter[parameterSize - 1] = parameterLastWord;
- for (i = 0; i < bitsNeeded; i++)
- DownLeft (parameter, parameterSize);
- }
-
-
- /*
- * The following routine hashes the input file, compares this
- * with the hash stored in the signature (to verify they are
- * equal), then re-computes the public key using the provided
- * authentication information, and finally returns the computed
- * public key.
- */
- Check(inputFile, sigFile, publicKey, dateTime32Ptr, msgBuffer)
- FILE *inputFile;
- FILE *sigFile;
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 *dateTime32Ptr;
- char msgBuffer[MAX_MESSAGE_SIZE_IN_BYTES];
- { int location;
- word32 pathDescription;
- int signatures;
- word32 outputCheckValue[MAX_SECURITY_SIZE];
- int hashMethod;
- int securitySize;
- int hashValueSize;
- word32 hashValue[MAX_HASH_VALUE_SIZE];
- int noOfCounts;
- int noOfSignatures;
- int noOfBitsPerVerifier;
- word32 parameter[MAX_PARAMETER_SIZE];
- word32 checkValue[MAX_SECURITY_SIZE];
- int parameterSize;
- word32 signature[MAX_SIGNATURE_SIZE];
- int i;
- word32 hashFromSig[MAX_HASH_VALUE_SIZE];
- word32 *hashPtr;
- int chunkSize;
- word32 wordsToHash;
- int binaryDataFieldSize;
- #if SHOW_TIMES
- word32 startTime, stopTime;
- int signRepeatIndex;
- double signTime;
- #endif
-
- /* self-test, to make sure everything is okay. */
- DoSelfTest (SELF_TEST);
- /* Check the digitally signed message */
-
- #if SHOW_TIMES
- startTime = GetCpuTime();
- for(signRepeatIndex=0; signRepeatIndex<1000; signRepeatIndex++) {
- #endif
- ReadSignature (sigFile, &hashMethod,
- &noOfSignatures, parameter, ¶meterSize,
- &securitySize, &hashValueSize, &noOfCounts,
- &noOfBitsPerVerifier, signature, &location,
- &wordsToHash, hashFromSig, dateTime32Ptr,
- &binaryDataFieldSize, msgBuffer);
-
- HashFile (inputFile, hashValue, hashValueSize, hashMethod);
- for (i=0; i<hashValueSize; i++)
- if (hashValue[i] != hashFromSig[i])
- ErrAbort("File or signature has been changed");
- hashPtr = signature;
- for (i=0; i<hashValueSize; i++) hashValue[i] = 0;
- i = wordsToHash;
- chunkSize = INPUT_BLOCK_SIZE-hashValueSize;
- while (i >= chunkSize) {
- HashTwoItems(hashValue, hashValueSize,
- hashValue, hashValueSize,
- hashPtr, chunkSize, hashMethod);
- hashPtr += chunkSize;
- i -= chunkSize;
- };
- if (i>0) HashTwoItems(hashValue, hashValueSize,
- hashValue, hashValueSize,
- hashPtr, i, hashMethod);
- HashTwoItems(hashValue, hashValueSize,
- hashValue, hashValueSize,
- &wordsToHash, 1, hashMethod);
- #if DEBUG
- PrintIt ("hashValue=", hashValue, hashValueSize);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
- /* initialize checkValue */
- HashTwoItems (checkValue, securitySize,
- hashValue, hashValueSize,
- parameter, parameterSize,
- hashMethod);
- for (signatures = 0; signatures < noOfSignatures; signatures++) {
- /* Set parameter for one-time signature */
- DownLeft (parameter, parameterSize);
- UpLeft (parameter, parameterSize);
- #if DEBUG
- printf ("\nAbout to check one time signature\n");
- PrintIt ("checkValue=", checkValue, securitySize);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
- OneTimeSignCheck (checkValue, securitySize,
- parameter, parameterSize, CHECK,
- noOfCounts, noOfBitsPerVerifier,
- &signature[location], outputCheckValue, hashMethod);
- Copy (checkValue, outputCheckValue, securitySize);
- #if DEBUG
- printf ("\nfinished checking one time signature\n");
- PrintIt ("checkValue=", checkValue, securitySize);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
- location += securitySize * noOfCounts;
- pathDescription = signature[location];
- location++; /* skip the path description */
- DownRight (parameter, parameterSize);
- CheckAuthenticationPath (checkValue, securitySize, parameter,
- parameterSize, hashMethod,
- pathDescription, &signature[location]);
- /* now skip past the authentication path */
- location -= securitySize; /* fix off-by-one error */
- while (pathDescription != 0) {
- pathDescription >>= 1;
- location += securitySize;
- };
- };
- #if SHOW_TIMES
- };
- stopTime = GetCpuTime();
- signTime = stopTime-startTime;
- signTime = signTime/1.0e6;
- fprintf(stderr, "%9.3f milliseconds to read and check signature\n",
- signTime);
- #endif
- #if DEBUG
- printf ("about to print results\n");
- PrintIt ("checkValue=", checkValue, securitySize);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
- #if SHOW_TIMES
- startTime = GetCpuTime();
- for(signRepeatIndex=0; signRepeatIndex<1000; signRepeatIndex++) {
- #endif
- publicKey[0] = signature[0];
- Copy(&publicKey[1], checkValue, securitySize);
- Copy(&publicKey[1+securitySize], parameter, parameterSize);
- #if SHOW_TIMES
- };
- stopTime = GetCpuTime();
- signTime = stopTime-startTime;
- signTime = signTime/1.0e6;
- fprintf(stderr, "%9.3f milliseconds for look up in public file\n",
- signTime);
- #endif
- }
-
- /*
- * The following routine dumps a signature in a readable format.
- */
- void
- DumpSig(sigFile)
- FILE *sigFile;
- { int location;
- word32 pathDescription;
- int signatures;
- int hashMethod;
- int securitySize;
- int hashValueSize;
- int noOfCounts;
- int noOfSignatures;
- int noOfBitsPerVerifier;
- word32 parameter[MAX_PARAMETER_SIZE];
- int parameterSize;
- word32 signature[MAX_SIGNATURE_SIZE];
- int i;
- int j;
- word32 dateTime32;
- char message[MAX_MESSAGE_SIZE_IN_BYTES];
- word32 hashFromSig[MAX_HASH_VALUE_SIZE];
- word32 wordsToHash;
- int binaryDataFieldSize;
- long longTemp;
- char dateString[80];
-
- ReadSignature (sigFile, &hashMethod,
- &noOfSignatures, parameter, ¶meterSize,
- &securitySize, &hashValueSize, &noOfCounts,
- &noOfBitsPerVerifier, signature, &location,
- &wordsToHash, hashFromSig, &dateTime32,
- &binaryDataFieldSize, message);
-
- printf(" hash method = %d\n", hashMethod);
- printf(" number of counts = %d\n", noOfCounts);
- printf(" no of bits per verifier = %d\n", noOfBitsPerVerifier);
- printf(" number of signatures = %d\n", noOfSignatures);
- printf(" location of first sig = %d\n", location);
- longTemp = dateTime32;
- strcpy(dateString, ctime(&longTemp));
- if (dateString[strlen(dateString)-1] == '\n')
- dateString[strlen(dateString)-1] = (char) 0;
- printf(" date of signature = %s\n", dateString);
- printf(" message = \"%s\"\n", message);
- printf(" words to hash = %d\n", wordsToHash);
- printf(" binary data field size = %d\n", binaryDataFieldSize);
- printf(" security size = %d\n", securitySize);
- printf(" parameter size = %d\n", parameterSize);
- printf(" parameter =");
- for (i=0; i<parameterSize; i++)
- printf(" %08lx", parameter[i]);
- printf("\n hash value size = %d\n", hashValueSize);
- printf(" hash value =");
- for (i=0; i<hashValueSize; i++)
- printf(" %08lx", hashFromSig[i]);
- printf("\n\n");
-
-
- for (signatures = 0; signatures < noOfSignatures; signatures++) {
- printf("\n authenticators:\n");
- for (i=0; i<noOfCounts; i++) {
- printf(" ");
- for (j=0; j<securitySize; j++) {
- printf(" %08lx",
- signature[location+i*securitySize+j]);
- };
- printf("\n");
- };
- location += securitySize * noOfCounts;
- pathDescription = signature[location];
- printf("\n path description = %08lx\n", pathDescription);
- location++; /* skip the path description */
- /* now skip past the authentication path */
- if (pathDescription != 0) while (pathDescription != 1) {
- printf(" ");
- for (i=0; i<securitySize; i++)
- printf(" %08lx", signature[location+i]);
- printf("\n");
- pathDescription >>= 1;
- location += securitySize;
- };
- };
- }
-
-
-
- /* The signing routines follow this point */
-
-
- /* The following routine emptys the signature buffer */
- /* All it does is set the "next available word" pointer */
- /* to 0 */
- void
- EmptySignatureBuffer (sigBuffer)
- struct SIG_BUF *sigBuffer;
- {
- sigBuffer->locInSignature = 0;
- }
-
- /* Places the passed in array into the signature buffer. */
- void
- PutInSignatureBuffer (array, size, sigBuffer)
- word32 array[];
- int size;
- struct SIG_BUF *sigBuffer;
- { int i;
-
- if (sigBuffer->locInSignature + size >= MAX_SIGNATURE_SIZE)
- ErrAbort (" overflow of authentication path");
- for (i = 0; i < size; i++)
- sigBuffer->signature[sigBuffer->locInSignature + i] = array[i];
- sigBuffer->locInSignature += size;
- }
-
- void
- GenPathForOTT (depth, top, securitySize, noOfCounts,
- path, countVerifiers,
- authPathValues,
- sigBuffer)
- int depth;
- int top;
- int securitySize;
- int noOfCounts;
- word32 path[MAX_STACK_DEPTH];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- struct SIG_BUF *sigBuffer;
- { int i;
- word32 temp;
-
- /* spit out the count verifiers */
- if (depth < top)
- for (i = 0; i < noOfCounts; i++) {
- #if DEBUG
- PrintIt ("countVerifier=",
- countVerifiers[depth + 1][i], securitySize);
- #endif
- PutInSignatureBuffer (countVerifiers[depth + 1][i],
- securitySize, sigBuffer);
- };
-
- /* and the path description */
- #if DEBUG
- PrintIt ("path=", &path[depth], 1);
- #endif
- PutInSignatureBuffer (&path[depth], 1, sigBuffer);
-
- /* and then the authentication path */
- temp = path[depth];
- for (temp = path[depth]; temp != 1; temp >>= 1) {
- #if DEBUG
- PrintIt ("authPathValue=",
- authPathValues[depth][temp ^ 1], securitySize);
- #endif
- PutInSignatureBuffer (authPathValues[depth][temp ^ 1],
- securitySize, sigBuffer);
- };
- }
-
- void
- GenerateAuthenticationPath (top, securitySize,
- noOfCounts,
- path, countVerifiers,
- authPathValues, sigBuffer)
- int top;
- int securitySize;
- int noOfCounts;
- word32 path[MAX_STACK_DEPTH];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- struct SIG_BUF *sigBuffer;
- { int depth;
- for (depth = top; depth >= 0; depth--)
- GenPathForOTT (depth,
- top, securitySize,
- noOfCounts, path,
- countVerifiers, authPathValues, sigBuffer);
- }
-
- /*
- * Read in the information from the auxilliary file (which is stored
- * in binary) and convert it into some useful internal format.
- */
- void
- ReadBinaryAuxInfo (top, securitySize, noOfCounts, OTTsize, authPathValues,
- countVerifiers, toggle)
- int top;
- int securitySize;
- int noOfCounts;
- int OTTsize;
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- int toggle;
- { int readReturnCode;
- FILE *auxInfoFile;
- int i,j,k,loc;
- word32 temp[MAX_STACK_DEPTH*MAX_SECURITY_SIZE*2*MAX_OTT_SIZE];
-
-
- if (2*MAX_OTT_SIZE < MAX_NO_OF_COUNTS)
- ErrAbort("2*MAX_OTT_SIZE < MAX_NO_OF_COUNTS");
- RangeCheck (top, 0, MAX_STACK_DEPTH - 1, "top");
- auxInfoFile = fopen (AuxInfoFile(toggle), "rb");
- if (auxInfoFile == NULL)
- ErrAbort ("Can't open aux info file for read");
- readReturnCode = fread((char*)temp, 1,
- top*noOfCounts*securitySize*sizeof(word32), auxInfoFile);
- if (readReturnCode !=
- top*noOfCounts*securitySize*sizeof(word32)) {
- fprintf(stderr,
- "Can't read verifiers from %s\n", AuxInfoFile(toggle));
- exit(1);
- };
- loc = 0;
- for(i=1; i<=top; i++)
- for(j=0; j<noOfCounts; j++)
- for(k=0; k<securitySize; k++)
- countVerifiers[i][j][k] = temp[loc++];
-
-
- readReturnCode = fread((char*)temp, 1,
- (top+1)*2*OTTsize*securitySize*sizeof(word32), auxInfoFile);
- if (readReturnCode!=
- (top+1)*2*OTTsize*securitySize*sizeof(word32)) {
- fprintf(stderr,
- "Can't read authPathValues from %s\n",
- AuxInfoFile(toggle));
- exit(1);
- };
- loc = 0;
- for(i=0; i<=top; i++)
- for(j=0; j<2*OTTsize; j++)
- for(k=0; k<securitySize; k++)
- authPathValues[i][j][k] = temp[loc++];
-
- FileClose(auxInfoFile, AuxInfoFile(toggle));
- }
-
- /* Generate pseudo-random "secret X values" and put them into
- * "xyVector". The number of elements in "xyVector" is given
- * by "elementsInXVector", while the size of each element
- * (which will typically be 2, the normal size for securitySize)
- * is given by "sizeOfXYElementInWords". The two inputs, "bits1"
- * and "bits2" hold the "seed" information from which to generate
- * the pseudo-random secret X values.
- */
- void
- GenerateSecretXValues (
- xyVector, elementsInXVector, sizeOfXYElementInWords,
- bits1, sizeOfBits1,
- bits2, sizeOfBits2,
- hashMethod)
- word32 xyVector[MAX_NO_OF_COUNTS * MAX_SECURITY_SIZE];
- int elementsInXVector;
- int sizeOfXYElementInWords;
- word32 bits1[];
- int sizeOfBits1;
- word32 bits2[];
- int sizeOfBits2;
- int hashMethod;
- { int i;
- word32 tempBlock[MAX_INPUT_BLOCK_SIZE];
-
- /* Error checking */
- if (sizeOfBits1+sizeOfBits2 > MAX_INPUT_BLOCK_SIZE)
- ErrAbort("Input size too large to generate xyVector");
- /* zero out the tempBlock bits */
- for (i = 0; i < MAX_INPUT_BLOCK_SIZE; i++)
- tempBlock[i] = 0;
- /* and copy the bits to tempBlock */
- Copy( tempBlock, bits1, sizeOfBits1);
- Copy(&tempBlock[sizeOfBits1], bits2, sizeOfBits2);
- /*
- * generate some secret random bits by hashing together the input
- * bits, and putting them in xyVector.
- */
- HashExpand (xyVector, elementsInXVector * sizeOfXYElementInWords,
- tempBlock, sizeOfBits1+sizeOfBits2, hashMethod);
- }
-
-
- /*
- * The following routine takes a set of counts in the array "count,"
- * determines how big the "check" field should be, and how many elements near
- * the end of the "count" array it should occupy, computes the "check" field
- * and appends the needed bits to the end of "count"
- */
- FillInTheCheckField (count, noOfCounts,
- securitySize, bitsPerVerifier,
- twoToTheBitsPerVerifier)
- word32 count[ /* noOfCounts */ ];
- int noOfCounts;
- int securitySize;
- int bitsPerVerifier;
- word32 twoToTheBitsPerVerifier;
- { word32 sumOfCounts;
- word32 maxPossibleSumOfCounts;
- int startOfCheckFieldInCountArray;
- int sizeOfCheckFieldInBits;
- int checkFieldCounts;
- int startOfCheckFieldInBits;
- int i;
-
- /*
- * figure out where the check field starts (the offset in the "count"
- * array)
- */
- startOfCheckFieldInCountArray =
- (WORD_SIZE_IN_BITS * securitySize - 1) / bitsPerVerifier + 1;
- /* compute the maximum possible sum of counts */
- maxPossibleSumOfCounts = startOfCheckFieldInCountArray *
- (twoToTheBitsPerVerifier - 1);
- /*
- * figure out how many bits are in the check field by seeing how many
- * bits it takes to hold the maximum possible sum of counts
- */
- sizeOfCheckFieldInBits = SizeInBits (maxPossibleSumOfCounts);
- /* and figure out how many counts that takes */
- checkFieldCounts = (sizeOfCheckFieldInBits - 1) / bitsPerVerifier + 1;
- /* make sure the check field will fit! */
- if (startOfCheckFieldInCountArray + checkFieldCounts != noOfCounts)
- ErrAbort ("adjust noOfCounts");
-
- /* There shouldn't be anything in the check field right now */
- for (i = startOfCheckFieldInCountArray; i < noOfCounts; i++)
- if (count[i] != 0)
- ErrAbort ("Undefined check field");
- /* add up the total value of the counts */
- sumOfCounts = 0;
- for (i = 0; i < noOfCounts; i++)
- sumOfCounts += count[i];
- /* and reverse it */
- sumOfCounts = maxPossibleSumOfCounts - sumOfCounts;
- /* figure out where the bits start */
- startOfCheckFieldInBits = WORD_SIZE_IN_BITS -
- checkFieldCounts * bitsPerVerifier;
- /*
- * The following is not intrinsically wrong -- I'm just not going to
- * bother handling a check field bigger than 32 bits. I figure it
- * should never happen....
- */
- if (startOfCheckFieldInBits <= 0)
- ErrAbort ("Check field overflowed 32 bits");
- /*
- * Okay, now we know the start and size of the "check" field, grab
- * the bits and put them into the "count" array at the proper spot
- */
- for (i = startOfCheckFieldInCountArray; i < noOfCounts; i++) {
- count[i] = FetchBits (&sumOfCounts, 1,
- startOfCheckFieldInBits, bitsPerVerifier);
- startOfCheckFieldInBits += bitsPerVerifier;
- };
- if (startOfCheckFieldInBits < WORD_SIZE_IN_BITS)
- ErrAbort ("check field wouldn't fit");
- }
-
- /* The following routine finishes an OTT (One Time Tree). An
- * OTT consists of several one-time signatures combined in a
- * fixed-sized tree pattern by the use of one-way hash functions.
- * (See, for example, "A Digital Signature Based on a Conventional
- * Encryption Function" by Ralph C. Merkle, Crypto '87, page 369).
- * This routine generates all of the needed one-time signatures, and
- * computes the required hash value for the tree. In addition, once
- * the hash value for this OTT has been computed, the signature for
- * the hash value, which must be generated using the appropriate
- * one-time signature from the previous OTT, is also computed.
- */
- void
- FinishNewOTT (
- top,
- securitySize,
- parameterSize,
- hashMethod,
- noOfCounts,
- noOfBitsPerVerifier,
- OTTsize,
- authPathValues,
- OTTparameterTop,
- countVerifiers,
- secretKey,
- secretKeySize
- )
- int top;
- int securitySize;
- int parameterSize;
- int hashMethod;
- int noOfCounts;
- int noOfBitsPerVerifier;
- int OTTsize;
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- word32 OTTparameterTop[MAX_PARAMETER_SIZE];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- word32 secretKey[SECRET_KEY_SIZE];
- int secretKeySize;
- { word32 i, j;
- word32 parameter[MAX_PARAMETER_SIZE];
- word32 dummyValueToSign[MAX_SECURITY_SIZE];
- word32 throwAwayHashResult[MAX_SECURITY_SIZE];
- word32 xyVector[MAX_NO_OF_COUNTS * MAX_SECURITY_SIZE];
-
- for (i = 0; i < MAX_SECURITY_SIZE; i++)
- dummyValueToSign[i] = 0;
-
- /*
- * loop through for each one-time signature in the OTT and compute
- * its authentication value
- */
- for (i = OTTsize; i < 2 * OTTsize; i++) {
- Copy (parameter, OTTparameterTop, parameterSize);
- UpPath (parameter, parameterSize, i);
- UpLeft (parameter, parameterSize);
- /* generate the secret X values with which we sign things */
- GenerateSecretXValues (xyVector, noOfCounts,
- securitySize,
- parameter, parameterSize,
- secretKey, secretKeySize,
- hashMethod);
- #if DEBUG
- printf ("\ngenerating authPath value\n");
- PrintIt ("path=", &i, 1);
- PrintIt ("secretKey=", secretKey, secretKeySize);
- PrintIt ("xyVector[last]=",
- &xyVector[noOfCounts * securitySize - 1], 1);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
- OneTimeSignCheck (dummyValueToSign, securitySize,
- parameter, parameterSize, COMPUTE_OTT_HASH,
- noOfCounts, noOfBitsPerVerifier,
- xyVector, authPathValues[top][i], hashMethod);
-
- #if DEBUG
- printf ("\done generating authPath value\n");
- PrintIt ("xyVector[last]=",
- &xyVector[noOfCounts * securitySize - 1], 1);
- PrintIt ("parameter=", parameter, parameterSize);
- PrintIt ("authPathValues[..]=",
- authPathValues[top][i], securitySize);
- #endif
- };
-
- for (i = OTTsize - 1; i > 0; i--) {
- Copy (parameter, OTTparameterTop, parameterSize);
- UpPath (parameter, parameterSize, i);
- #if DEBUG
- printf ("\ngenerating non-leaves in authPathValues\n");
- PrintIt ("path=", &i, 1);
- PrintIt ("parameter=", parameter, parameterSize);
- PrintIt ("authPathValues[top][2*path]=",
- authPathValues[top][2 * i], securitySize);
- PrintIt ("authPathValues[top][2*path+1]=",
- authPathValues[top][2 * i + 1],
- securitySize);
- #endif
- HashThreeItems (
- authPathValues[top][i], securitySize,
- authPathValues[top][2 * i], securitySize,
- authPathValues[top][2 * i + 1], securitySize,
- parameter, parameterSize,
- hashMethod);
-
- #if DEBUG
- printf ("\just-computed parent is:\n");
- PrintIt ("authPathValues[top][path]=",
- authPathValues[top][i], securitySize);
- #endif
- };
-
- if (top > 0) {
- /*
- * gotta add the signature from the previous OTT to this
- * OTT
- */
- Copy (parameter, OTTparameterTop, parameterSize);
- DownLeft (parameter, parameterSize);
- UpLeft (parameter, parameterSize);
- /* generate the secret X values with which we sign things */
- GenerateSecretXValues (xyVector, noOfCounts,
- securitySize,
- parameter, parameterSize,
- secretKey, secretKeySize,
- hashMethod);
-
- OneTimeSignCheck (authPathValues[top][1],
- securitySize,
- parameter, parameterSize, SIGN,
- noOfCounts, noOfBitsPerVerifier,
- xyVector, throwAwayHashResult,
- hashMethod);
-
- for (i = 0; i < noOfCounts; i++)
- for (j = 0; j < securitySize; j++)
- countVerifiers[top][i][j] =
- xyVector[i * securitySize + j];
- };
-
- }
-
- /*
- * Write out the "aux Info" file in binary. Convert the internal
- * information describing the signing "state" into a suitable binary
- * output format. Note that the information in the auxilliary file
- * is not security sensitive, need not be kept secret, and can even
- * be modified. If the information is modified, generated signatures
- * will be invalid (which will be rapidly detected) but security of
- * valid signatures will not be compromised. Note that in principle
- * the incorrectly generated signatures could be repaired by determining
- * the correct authentication paths to use.
- */
- void
- WriteBinaryAuxInfo(top, securitySize, noOfCounts, OTTsize, authPathValues,
- countVerifiers, toggle)
- int top;
- int securitySize;
- int noOfCounts;
- int OTTsize;
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- int toggle;
- { FILE *auxInfoFile;
- int writeReturnCode;
- word32 temp[MAX_STACK_DEPTH*MAX_SECURITY_SIZE*2*MAX_OTT_SIZE];
- int i,j,k,loc;
-
- if (2*MAX_OTT_SIZE < MAX_NO_OF_COUNTS)
- ErrAbort("2*MAX_OTT_SIZE < MAX_NO_OF_COUNTS");
- RangeCheck (top, 0, MAX_STACK_DEPTH - 1, "top");
- auxInfoFile = fopen (AuxInfoFile(toggle), "wb");
- if (auxInfoFile == NULL)
- ErrAbort ("Can't open aux info file for write");
- loc = 0;
- for(i=1; i<=top; i++)
- for(j=0; j<noOfCounts; j++)
- for(k=0; k<securitySize; k++)
- temp[loc++] = countVerifiers[i][j][k];
- writeReturnCode = fwrite((char*)temp, 1,
- top*noOfCounts*securitySize*sizeof(word32), auxInfoFile);
- if (writeReturnCode !=
- top*noOfCounts*securitySize*sizeof(word32)) {
- fprintf(stderr,
- "Can't write verifiers to %s\n", AuxInfoFile(toggle));
- exit(1);
- };
- loc = 0;
- for(i=0; i<=top; i++)
- for(j=0; j<2*OTTsize; j++)
- for(k=0; k<securitySize; k++)
- temp[loc++] = authPathValues[i][j][k];
- writeReturnCode = fwrite((char*)temp, 1,
- (top+1)*2*OTTsize*securitySize*sizeof(word32), auxInfoFile);
- if (writeReturnCode !=
- (top+1)*2*OTTsize*securitySize*sizeof(word32)) {
- fprintf(stderr,
- "Can't write authPathValues to %s\n",
- AuxInfoFile(toggle));
- exit(1);
- };
- FileSyncAndClose(auxInfoFile, AuxInfoFile(toggle));
- }
-
- /*
- * Write out the signature buffer into the signature file.
- */
- void
- WriteSignatureBuffer (sigFile, sigBuffer)
- FILE *sigFile;
- struct SIG_BUF *sigBuffer;
- { int printCode;
- char charSignature[MAX_SIGNATURE_SIZE * 4];
-
- /* write it! */
- ConvertLongToChar (sigBuffer->signature, charSignature,
- sigBuffer->locInSignature);
- printCode = fwrite (charSignature, 4, sigBuffer->locInSignature,
- sigFile);
- if (printCode != sigBuffer->locInSignature)
- ErrAbort ("can't write auth path to *.sig");
- }
-
- /*
- * The following routine simply generates some more-or-less random bits and
- * puts them into "randomOutput".
- *
- * Notice that this routine is adequate ONLY IF "machineKey" IS NOT GOING TO
- * BE KEPT SECRET. If "machineKey" is going to be kept secret, then a more
- * random method of generating random numbers is required here.
- *
- * Whether or not to keep "machineKey" secret is a system design issue not
- * addressed here. It will often be desirable to keep it secret.
- *
- * Many implementors will well wish to use a better "random number" generator
- * than hashing the time....
- */
- void
- AssignRandom (randomOutput, sizeOfRandomOutputInWords,
- moreBits, sizeOfMoreBitsInWords)
- word32 randomOutput[];
- int sizeOfRandomOutputInWords;
- word32 moreBits[];
- int sizeOfMoreBitsInWords;
- { word32 unSignedTime;
-
- if (sizeOfRandomOutputInWords > 8)
- ErrAbort ("can't generate enough random bits");
- if (sizeOfRandomOutputInWords >
- (sizeOfMoreBitsInWords + SECRET_KEY_SIZE))
- ErrAbort ("too few input bits to generate random bits");
- unSignedTime = GetTime32();
- /*
- * hash together the time and whatever extra bits were passed in to
- * us
- */
- HashTwoItems (randomOutput, sizeOfRandomOutputInWords,
- moreBits, sizeOfMoreBitsInWords,
- &unSignedTime, 1, 4);
- /* randomOutput should now be reasonably random. */
- }
-
- /*
- * Push an OTT (One Time Tree) onto the stack of One Time Trees.
- * Mostly, this is a housekeeping routine that pushes the stack,
- * initializes one or two critical variables in the OTT, and then
- * calles "FinishNewOTT".
- */
- void
- PushOTT (
- topPtr,
- securitySize,
- parameterSize,
- hashMethod,
- noOfCounts,
- noOfBitsPerVerifier,
- path,
- authPathValues,
- OTTparameterTop,
- countVerifiers,
- secretKey,
- secretKeySize,
- OTTsize
- )
- int *topPtr;
- int securitySize;
- int parameterSize;
- int hashMethod;
- int noOfCounts;
- int noOfBitsPerVerifier;
- word32 path[MAX_STACK_DEPTH];
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- word32 OTTparameterTop[MAX_PARAMETER_SIZE];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- word32 secretKey[SECRET_KEY_SIZE];
- int secretKeySize;
- int OTTsize;
- { UpPath (OTTparameterTop, parameterSize, path[*topPtr]);
- UpRight (OTTparameterTop, parameterSize);
- (*topPtr)++;
- if ((*topPtr) >= MAX_STACK_DEPTH)
- ErrAbort ("stack overflow");
-
- FinishNewOTT (*topPtr, securitySize,
- parameterSize, hashMethod,
- noOfCounts, noOfBitsPerVerifier,
- OTTsize, authPathValues,
- OTTparameterTop, countVerifiers,
- secretKey, secretKeySize);
- }
-
-
- /*
- * The following routine determines how much (if any) the stack of OTTs
- * should be allowed to grow.
- *
- * The following method was chosen so that an infinite number of messages can be
- * signed while the depth (size) of the signature grows logarithmically. It
- * would also be possible to have this routine return a simple fixed constant
- * (like 4), in which case all signatures would have the same size (a stack
- * depth of 4) but the number of signatures would be fixed and finite.
- *
- * This method of generating signatures also has the useful property that an OTT
- * can be generated once, used for a while, and then forgotten. Other
- * methods of generating signatures (other patterns of signatures within the
- * tree) result in the need to periodically re-create OTTs. While no great
- * burden, it seemed easier to avoid this situation.
- *
- * A small modification was made -- MIN_DEPTH is the minimum value that
- * this function will return. This is useful when it is known that some
- * fixed number of messages will be signed, so there is no point in generating
- * very small signatures initially (with top = 0, say) when this
- * will simply force longer signatures later.
- * In other words, the tree of OTTs will
- * always have at least a certain depth (from the root OTT to the leaf OTT).
- */
- int
- ComputeNewStackTop (top, path)
- int top;
- word32 path[MAX_STACK_DEPTH];
- { int i;
- int newTop;
- int bitsInPath;
- word32 signatureNumber;
- word32 signaturesAllowed;
- int depthToAdd;
-
-
- /*
- * fancy computation basically determines how much stack depth to add
- * for the level we're looking at in the stack.
- *
- * Example: if the number of one-time signatures allowed per OTT is
- * always 32, then we want the following pattern for the first level:
- */
- /**
- 32+0 <= path[0] < 32+16: desired top: 0
- 32+16 <= path[0] < 32+24: desired top: 1
- 32+24 <= path[0] < 32+28: desired top: 2
- 32+28 <= path[0] < 32+30: desired top: 3
- 32+30 <= path[0] < 32+31: desired top: 4
- 32+31 = path[0] desired top: look at next level
- */
- /*
- * Note: "path[0]" enumerates the leaves of this OTT (the one time
- * signatures) by encoding the first path as 32, the second path as
- * 33, etc. etc. The last path is numbered 63. There are a total of
- * 32 paths: numbered 32 to 63.
- *
- */
-
- newTop = 0;
- for (i = 0; i <= top; i++) {
-
- /* Figure out where the top bit is */
- bitsInPath = SizeInBits (path[i]) - 1;
-
- /* Make a mask of just the top bit */
- signaturesAllowed = (word32) 1 << bitsInPath;
-
- /* Now knock off the top bit */
- signatureNumber = path[i] ^ signaturesAllowed;
-
- if (signatureNumber >= signaturesAllowed)
- ErrAbort ("logic error: sigNum >= sigsAllowed");
-
- /* And compute how much more depth to add to the stack */
- depthToAdd = bitsInPath -
- SizeInBits (signaturesAllowed - signatureNumber - 1);
-
- if (depthToAdd < 0)
- ErrAbort ("logic error: depthToAdd < 0");
-
- newTop += depthToAdd;
-
- /*
- * If we're using the very last signature at this depth, we
- * need to look at the next depth to compute new top.
- * Otherwise, we know the value of newTop right now and
- * should return.
- */
- if (signaturesAllowed > signatureNumber + 1) {
- /* force depth to MIN_DEPTH no matter what */
- if (newTop < MIN_DEPTH) newTop = MIN_DEPTH;
- return (newTop);
- }
- };
- /* force depth to MIN_DEPTH no matter what */
- if (newTop < MIN_DEPTH) newTop = MIN_DEPTH;
- return (newTop);
- }
-
-
-
- /*
- * The following routine reads and hashes the input (taken from
- * "inputFile") and generates a valid signature for that hash.
- * The valid signature is written into the file: "sigFile".
- * The provided userKeyString is a secret key provided by the
- * user. This user-provided secret key is combined with the
- * "machineKey" (which might or might not be secret, depending
- * on the desires of the particular user/site) to generate the
- * secretKey which is actually used to sign messages.
- * Much of the code is taken up
- * with the trivia of error checking, command-line parameter checking,
- * self-tests, and the like.
- *
- */
-
- void
- Sign (userKeyString, message, inputFile, sigFile)
- char *userKeyString;
- char message[MAX_MESSAGE_SIZE_IN_BYTES];
- FILE *inputFile;
- FILE *sigFile;
- { int top;
- int newStackTop;
- int i;
- word32 parameter[MAX_PARAMETER_SIZE];
- word32 throwAwayHashResult[MAX_SECURITY_SIZE];
- word32 temp;
- int updateAuxInfo;
- int hashMethod;
- int securitySize;
- int hashValueSize;
- word32 hashValue[MAX_HASH_VALUE_SIZE];
- char charUserKey[USER_KEY_SIZE_IN_BYTES];
- word32 userKey[USER_KEY_SIZE];
- word32 secretKey[SECRET_KEY_SIZE];
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 hashedUserKey[HASHED_USER_KEY_SIZE];
- word32 machineKey[MACHINE_KEY_SIZE];
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- word32 path[MAX_STACK_DEPTH];
- int noOfCounts;
- int noOfBitsPerVerifier;
- word32 rootParameter[MAX_PARAMETER_SIZE];
- word32 checkValue[MAX_SECURITY_SIZE];
- word32 root[MAX_SECURITY_SIZE];
- word32 xyVector[MAX_NO_OF_COUNTS * MAX_SECURITY_SIZE];
- int parameterSize;
- struct SIG_BUF sigBuffer[1];
- word32 tempHSC[HASHED_USER_KEY_SIZE];
- word32 dateTime32;
- word32 wordMessage[MAX_MESSAGE_SIZE];
- int messageSize;
- int messageSizeInBytes;
- word32 wordsToHash;
- word32 *hashPtr;
- word32 binaryDataFieldSize;
- int startOfBinaryData;
- int endOfBinaryData;
- int chunkSize;
- char file1Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- char file2Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- FILE *file1;
- FILE *file2;
- int toggle;
- int OTTsize;
- #if SHOW_TIMES
- word32 startTime, stopTime;
- int signRepeatIndex;
- double signTime;
- int rememberedTop;
- #endif
-
- /* self-test, to make sure everything is okay. */
- DoSelfTest (SELF_TEST);
- strncpy(charUserKey, userKeyString, USER_KEY_SIZE_IN_BYTES);
- ConvertBytes (charUserKey, userKey, USER_KEY_SIZE_IN_BYTES);
- NameStableStorage(file1Name, file2Name);
- OpenAndLockTwoFiles(&file1, file1Name, &file2, file2Name);
- ReadStableStorage (file1, file2, machineKey, publicKey,
- hashedUserKey, path, &top, &toggle, &OTTsize);
- UnPackHeader (publicKey[0], &hashMethod,
- &securitySize, ¶meterSize,
- &noOfBitsPerVerifier, &hashValueSize, &noOfCounts);
- HashTwoItems(tempHSC, HASHED_USER_KEY_SIZE,
- userKey, USER_KEY_SIZE,
- userKey, USER_KEY_SIZE, hashMethod);
- for (i=0; i<20; i++) HashTwoItems(
- tempHSC, HASHED_USER_KEY_SIZE,
- tempHSC, HASHED_USER_KEY_SIZE,
- tempHSC, HASHED_USER_KEY_SIZE,
- hashMethod);
- for (i=0; i<HASHED_USER_KEY_SIZE; i++)
- if (tempHSC[i] != hashedUserKey[i])
- ErrAbort("Incorrect user key entered.");
- Copy(root, &publicKey[1], securitySize);
- Copy(rootParameter, &publicKey[1+securitySize], parameterSize);
- /*
- * Hash the user-provided key (presumed to be secret) and the
- * machine-readable key (security is enhanced if the machine key is
- * also secret. If the machine key is not secret, it still serves a
- * useful, though less important, function).
- *
- * The result is the "secretKey", from which all other secret
- * information is generated in a deterministic manner.
- */
-
- #if DEBUG
- PrintIt ("machine key is:",
- machineKey, MACHINE_KEY_SIZE);
- PrintIt ("user key is:",
- userKey, USER_KEY_SIZE);
- #endif
-
- HashTwoItems (secretKey, SECRET_KEY_SIZE,
- machineKey, MACHINE_KEY_SIZE,
- userKey, USER_KEY_SIZE,
- hashMethod);
- #if DEBUG
- PrintIt ("Secret key for signing is:", secretKey, SECRET_KEY_SIZE);
- #endif
- /* Generate the digital signature */
- #if SHOW_TIMES
- startTime = GetCpuTime();
- for(signRepeatIndex=0; signRepeatIndex<1000; signRepeatIndex++) {
- #endif
- ReadBinaryAuxInfo (top, securitySize, noOfCounts,
- OTTsize, authPathValues,
- countVerifiers, toggle);
- #if SHOW_TIMES
- };
- stopTime = GetCpuTime();
- signTime = stopTime-startTime;
- signTime = signTime/1.0e6;
- fprintf(stderr, "%9.3f milliseconds reading binary state\n",
- signTime);
- startTime = GetCpuTime();
- for(signRepeatIndex=0; signRepeatIndex<1000; signRepeatIndex++) {
- #endif
-
- HashFile (inputFile, hashValue, hashValueSize, hashMethod);
-
- #if SHOW_TIMES
- };
- stopTime = GetCpuTime();
- signTime = stopTime-startTime;
- signTime = signTime/1.0e6;
- fprintf(stderr, "%9.3f milliseconds hashing file\n",
- signTime);
- startTime = GetCpuTime();
- for(signRepeatIndex=0; signRepeatIndex<1000; signRepeatIndex++) {
- #endif
- /* get the root parameter */
- Copy (parameter, rootParameter, parameterSize);
-
- /* push the root parameter up the authentication path */
- for (i=0; i <= top; i++) {
- UpPath (parameter, parameterSize, path[i]);
- UpRight (parameter, parameterSize);
- };
-
- /* spit out the head of the signature */
- EmptySignatureBuffer (sigBuffer);
- PackHeader(&temp, hashMethod, securitySize,
- parameterSize, noOfBitsPerVerifier);
- PutInSignatureBuffer (&temp, 1, sigBuffer);
- if (top >= 255)
- ErrAbort ("Stack exceeds depth of 255");
- temp = top + 1;
- messageSizeInBytes = strlen(message)+1;
- messageSize = (messageSizeInBytes+3)>>2;
- binaryDataFieldSize = 1+hashValueSize+parameterSize;
- wordsToHash = 2+binaryDataFieldSize+messageSize;
- if ( (wordsToHash & 0xffff0000L) != 0)
- ErrAbort("message + stuff over 64 kilobytes.");
- temp |= (wordsToHash<<16);
- temp |= (binaryDataFieldSize << 8);
- PutInSignatureBuffer (&temp, 1, sigBuffer);
-
- startOfBinaryData = sigBuffer->locInSignature;
- #if DEBUG
- PrintIt ("\n initial parameter=", parameter, parameterSize);
- #endif
- dateTime32 = GetTime32();
- PutInSignatureBuffer (&dateTime32, 1, sigBuffer);
- PutInSignatureBuffer (hashValue, hashValueSize, sigBuffer);
- PutInSignatureBuffer (parameter, parameterSize, sigBuffer);
- /*
- * Additional binary fields of data can be added at this point by
- * inserting additional calls to "PutInSignatureBuffer". The additional
- * fields will be ignored by the signature checking program. To inform
- * the signature checking program of the size of the additional fields,
- * it is necessary to increase the size of "binaryDataFieldSize" by the
- * appropriate amount. It is computed above, and its value is checked
- * in the following two statements.
- */
- endOfBinaryData = sigBuffer->locInSignature;
- if ( (endOfBinaryData-startOfBinaryData) != binaryDataFieldSize)
- ErrAbort("error while generating size of binary data field");
- ConvertBytes(message, wordMessage, messageSizeInBytes);
- PutInSignatureBuffer (wordMessage, messageSize, sigBuffer);
- if (wordMessage[messageSize-1]&0xffL != 0)
- ErrAbort("wordMessage is not null terminated");
- hashPtr = sigBuffer->signature;
- for (i=0; i<hashValueSize; i++) hashValue[i] = 0;
- i = wordsToHash;
- chunkSize = INPUT_BLOCK_SIZE-hashValueSize;
- while (i >= chunkSize) {
- HashTwoItems(hashValue, hashValueSize,
- hashValue, hashValueSize,
- hashPtr, chunkSize, hashMethod);
- hashPtr += chunkSize;
- i -= chunkSize;
- };
- if (i>0) HashTwoItems(hashValue, hashValueSize,
- hashValue, hashValueSize,
- hashPtr, i,hashMethod);
- HashTwoItems(hashValue, hashValueSize,
- hashValue, hashValueSize,
- &wordsToHash, 1, hashMethod);
- /*
- * hash the double-sized value produced by the strong hash function
- * into the smaller value that is actually signed.
- */
- #if DEBUG
- printf ("SIGNING\n");
- PrintIt ("hashValue=", hashValue, hashValueSize);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
- HashTwoItems (checkValue, securitySize,
- hashValue, hashValueSize,
- parameter, parameterSize,
- hashMethod);
- DownLeft (parameter, parameterSize);
- UpLeft (parameter, parameterSize);
- /* generate the secret X values with which we sign things */
- GenerateSecretXValues (xyVector, noOfCounts, securitySize,
- parameter, parameterSize,
- secretKey, SECRET_KEY_SIZE, hashMethod);
-
- #if DEBUG
- printf ("\nAbout to generate one time signature\n");
- PrintIt ("path=", &path[top], 1);
- PrintIt ("secretKey=", secretKey, SECRET_KEY_SIZE);
- PrintIt ("xyVector[last]=",
- &xyVector[noOfCounts * securitySize - 1], 1);
- PrintIt ("checkValue=", checkValue, securitySize);
- PrintIt ("parameter=", parameter, parameterSize);
- #endif
-
- OneTimeSignCheck (checkValue, securitySize,
- parameter, parameterSize, SIGN,
- noOfCounts, noOfBitsPerVerifier,
- xyVector, throwAwayHashResult, hashMethod);
-
- #if DEBUG
- printf ("\nfinished generating one time signature\n");
- PrintIt ("xyVector[last]=",
- &xyVector[noOfCounts * securitySize - 1], 1);
- PrintIt ("checkValue (discarded)=",
- throwAwayHashResult, securitySize);
- #endif
- for (i = 0; i < noOfCounts; i++)
- PutInSignatureBuffer (&xyVector[i * securitySize],
- securitySize, sigBuffer);
- GenerateAuthenticationPath (top, securitySize,
- noOfCounts, path, countVerifiers,
- authPathValues, sigBuffer);
- #if SHOW_TIMES
- };
- stopTime = GetCpuTime();
- signTime = stopTime-startTime;
- signTime = signTime/1.0e6;
- fprintf(stderr, "%9.3f milliseconds generating signature\n",
- signTime);
- #endif
- /*
- * make sure the aux info is updated in preparation for the next
- * signature
- */
- updateAuxInfo = FALSE;
- if (BumpPath (&path[top]) == PATHOVERFLOW) {
- updateAuxInfo = TRUE;
- /*
- * We've run out of signatures in the top OTT. Do Something!
- */
- while (BumpPath (&path[top]) == PATHOVERFLOW) {
- top--;
- if (top < 0)
- ErrAbort ("Logic Error: no signatures");
- };
- };
- #if SHOW_TIMES
- startTime = GetCpuTime();
- rememberedTop = top;
- for(signRepeatIndex=0; signRepeatIndex<1000; signRepeatIndex++) {
- top = rememberedTop;
- #endif
- newStackTop = ComputeNewStackTop (top, path);
- if (newStackTop != top)
- updateAuxInfo = TRUE;
- #if DEBUG
- printf ("\n new top= %d\n", newStackTop);
- #endif
- /* get the root parameter */
- Copy (parameter, rootParameter, parameterSize);
- /* push the root parameter up the authentication path */
- for (i=0; i < top; i++) {
- UpPath (parameter, parameterSize, path[i]);
- UpRight (parameter, parameterSize);
- };
- while (top < newStackTop) {
- path[top+1] = OTTsize;
- PushOTT (&top, securitySize, parameterSize, hashMethod,
- noOfCounts, noOfBitsPerVerifier,
- path, authPathValues,
- parameter, countVerifiers,
- secretKey, SECRET_KEY_SIZE, OTTsize);
- };
- #if SHOW_TIMES
- };
- stopTime = GetCpuTime();
- signTime = stopTime-startTime;
- signTime = signTime/1.0e6;
- fprintf(stderr, "%9.3f milliseconds setting up for next signature\n",
- signTime);
- startTime = GetCpuTime();
- for(signRepeatIndex=0; signRepeatIndex<1000; signRepeatIndex++) {
- #endif
- if (updateAuxInfo == TRUE) {
- toggle ^= 1;
- WriteBinaryAuxInfo(top, securitySize,
- noOfCounts, OTTsize, authPathValues,
- countVerifiers, toggle);
- };
- /*
- * DON'T WRITE OUT THE SIGNATURE UNTIL AFTER UPDATING STABLE
- * STORAGE!!
- */
- WriteStableStorage (file1, file2, machineKey, publicKey,
- hashedUserKey, path, top, toggle, OTTsize);
- #if SHOW_TIMES
- };
- stopTime = GetCpuTime();
- signTime = stopTime-startTime;
- signTime = signTime/1.0e6;
- fprintf(stderr, "%9.3f milliseconds writing stable storage\n",
- signTime);
- #endif
- /* Hard to repeatedly close the file.... */
- FileSyncAndClose(file1, file1Name);
- FileSyncAndClose(file2, file2Name);
- #if SHOW_TIMES
- startTime = GetCpuTime();
- for(signRepeatIndex=0; signRepeatIndex<1000; signRepeatIndex++) {
- #endif
- WriteSignatureBuffer (sigFile, sigBuffer);
- #if SHOW_TIMES
- };
- stopTime = GetCpuTime();
- signTime = stopTime-startTime;
- signTime = signTime/1.0e6;
- fprintf(stderr, "%9.3f milliseconds writing state and signature\n",
- signTime);
- #endif
- };
-
- /* The following routine writes an entry to the public file. It
- * is used during generation of the public key. Note that the
- * "public file" is in fact a directory, and that the file names
- * in the directory are in fact derived from the public key
- * values, not from the user's name. This is convenient for various
- * odd reasons that are idiosyncratic to this signature method.
- * Each public key gets its own file in the directory. Note that
- * it is theoretically possible (but can be made arbitrarily
- * improbable) for two different public keys to have the same
- * file name in the public directory. If this happens, it's just
- * tough. You get an error message and are told to try again.
- * For those who find this offensive, the file name for the public
- * key could in fact encode the entire public key. In this case,
- * a collsion in the directory would be as improbable as two
- * public keys being generated that were in fact identical. This
- * probability can also be made as low as desired.
- */
- void
- WriteEntryToPublicFile (publicDirectoryName, name, publicKey,
- secretKey, secretKeySize)
- char *publicDirectoryName;
- char name[];
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 secretKey[];
- int secretKeySize;
- { int i;
- FILE *descriptor;
- int printReturnCode;
- int publicKeySize;
- char *publicFileName;
-
- /* open the "public file" entry for writing */
- publicFileName = MakePublicFileName(publicDirectoryName, publicKey);
- if (FileExists(publicFileName) == 0)
- ErrAbort("Highly improbable collision occured -- try again.");
- descriptor = fopen (publicFileName, "w");
- if (descriptor == NULL)
- ErrAbort ("Could not write entry to public file");
-
- printReturnCode = fprintf (descriptor, "#%s: %08lx",
- name, publicKey[0]);
- if (printReturnCode <= 0)
- ErrAbort ("bad write to public file");
-
- publicKeySize = ComputePublicKeySize(publicKey);
- for (i = 1; i < publicKeySize; i++) {
- printReturnCode = fprintf (descriptor,
- " %08lx", publicKey[i]);
- if (printReturnCode <= 0)
- ErrAbort ("bad public key write to public file");
- };
-
- printReturnCode = fprintf (descriptor, "\n");
- if (printReturnCode <= 0)
- ErrAbort ("bad string write to public file");
- FileClose (descriptor, publicFileName);
- };
-
- /* The following routine is used to recover the auxilliary information
- * if it is damaged or lost. The auxilliary information can be derived
- * from the information kept in stable storage.
- */
- void
- RecoverAuxInfo(userKeyString)
- char *userKeyString;
- { int top;
- int newStackTop;
- int i;
- int hashMethod;
- int securitySize;
- int hashValueSize;
- char charUserKey[USER_KEY_SIZE_IN_BYTES];
- word32 userKey[USER_KEY_SIZE];
- word32 secretKey[SECRET_KEY_SIZE];
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 hashedUserKey[HASHED_USER_KEY_SIZE];
- word32 machineKey[MACHINE_KEY_SIZE];
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- word32 path[MAX_STACK_DEPTH];
- int noOfCounts;
- int noOfBitsPerVerifier;
- word32 rootParameter[MAX_PARAMETER_SIZE];
- word32 root[MAX_SECURITY_SIZE];
- int parameterSize;
- word32 tempHSC[HASHED_USER_KEY_SIZE];
- char file1Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- char file2Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- FILE *file1;
- FILE *file2;
- int toggle;
- int OTTsize;
-
- strncpy(charUserKey, userKeyString, USER_KEY_SIZE_IN_BYTES);
- ConvertBytes (charUserKey, userKey, USER_KEY_SIZE_IN_BYTES);
- NameStableStorage(file1Name, file2Name);
- OpenAndLockTwoFiles(&file1, file1Name, &file2, file2Name);
- ReadStableStorage (file1, file2, machineKey, publicKey,
- hashedUserKey, path, &newStackTop, &toggle, &OTTsize);
- UnPackHeader (publicKey[0], &hashMethod,
- &securitySize, ¶meterSize,
- &noOfBitsPerVerifier, &hashValueSize, &noOfCounts);
- HashTwoItems(tempHSC, HASHED_USER_KEY_SIZE,
- userKey, USER_KEY_SIZE,
- userKey, USER_KEY_SIZE, hashMethod);
- for (i=0; i<20; i++) HashTwoItems(
- tempHSC, HASHED_USER_KEY_SIZE,
- tempHSC, HASHED_USER_KEY_SIZE,
- tempHSC, HASHED_USER_KEY_SIZE,
- hashMethod);
- for (i=0; i<HASHED_USER_KEY_SIZE; i++)
- if (tempHSC[i] != hashedUserKey[i])
- ErrAbort("Incorrect user key entered.");
- Copy(root, &publicKey[1], securitySize);
- Copy(rootParameter, &publicKey[1+securitySize], parameterSize);
- /*
- * Hash the user-provided key (presumed to be secret) and the
- * machine-readable key (security is enhanced if the machine key is
- * also secret. If the machine key is not secret, it still serves a
- * useful, though less important, function).
- *
- * The result is the "secretKey", from which all other secret
- * information is generated in a deterministic manner.
- */
-
- HashTwoItems (secretKey, SECRET_KEY_SIZE,
- machineKey, MACHINE_KEY_SIZE,
- userKey, USER_KEY_SIZE,
- hashMethod);
- /* initialize the top (i.e. 0th) OTT */
- top = 0;
- FinishNewOTT (top, securitySize, parameterSize, hashMethod,
- noOfCounts, noOfBitsPerVerifier, OTTsize,
- authPathValues, rootParameter, countVerifiers,
- secretKey, SECRET_KEY_SIZE);
- for (i=0; i<securitySize; i++)
- if(root[i] != authPathValues[0][1][i])
- ErrAbort("Recovery failed");
- while (top < newStackTop) {
- PushOTT (&top, securitySize, parameterSize, hashMethod,
- noOfCounts, noOfBitsPerVerifier,
- path, authPathValues,
- rootParameter, countVerifiers,
- secretKey, SECRET_KEY_SIZE, OTTsize);
- };
- WriteBinaryAuxInfo(top, securitySize,
- noOfCounts, OTTsize, authPathValues, countVerifiers, toggle);
- }
-
- /*
- * The following routine creates new signing key/checking key pair and writes
- * out the new initial aux info and the new public entry.
- */
- void
- CreateSignCheckPair (userKeyString, userName, publicDirName, hashMethod,
- securitySize, parameterSize, noOfBitsPerVerifier, OTTsize)
- char *userKeyString;
- char *userName;
- char *publicDirName;
- int hashMethod;
- int securitySize;
- int parameterSize;
- int noOfBitsPerVerifier;
- { int i;
- char charUserKey[USER_KEY_SIZE_IN_BYTES];
- word32 userKey[USER_KEY_SIZE];
- word32 secretKey[SECRET_KEY_SIZE];
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- word32 hashedUserKey[HASHED_USER_KEY_SIZE];
-
- word32 machineKey[MACHINE_KEY_SIZE];
- int top;
- int newStackTop;
- word32 authPathValues
- [MAX_STACK_DEPTH]
- [2 * MAX_OTT_SIZE]
- [MAX_SECURITY_SIZE];
- word32 countVerifiers
- [MAX_STACK_DEPTH]
- [MAX_NO_OF_COUNTS]
- [MAX_SECURITY_SIZE];
- word32 OTTparameter[MAX_STACK_DEPTH][MAX_PARAMETER_SIZE];
- word32 path[MAX_STACK_DEPTH];
- int noOfCounts;
- word32 rootParameter[MAX_PARAMETER_SIZE];
- word32 root[MAX_SECURITY_SIZE];
- word32 parameter[MAX_PARAMETER_SIZE];
- FILE *file1;
- FILE *file2;
- char file1Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- char file2Name[MAX_FILE_NAME_SIZE_IN_BYTES];
-
- RangeCheck(securitySize, 2, MAX_SECURITY_SIZE, "Security size");
- RangeCheck(parameterSize, 2, MAX_PARAMETER_SIZE, "Parameter size");
- RangeCheck(noOfBitsPerVerifier, 1, MAX_BITS_PER_VERIFIER,
- "no of bits per verifier");
- RangeCheck(OTTsize, 4, MAX_OTT_SIZE, "OTT size");
- if ( ((OTTsize-1) & OTTsize) != 0) {
- fprintf(stderr, "OTT size is %d, not a power of 2\n",
- OTTsize);
- ErrAbort("");
- };
- strncpy(charUserKey, userKeyString, USER_KEY_SIZE_IN_BYTES);
- ConvertBytes (charUserKey, userKey, USER_KEY_SIZE_IN_BYTES);
- top = 0;
- noOfCounts = ComputeNumberOfCounts (
- securitySize, WORD_SIZE_IN_BITS, noOfBitsPerVerifier);
- /* generate the machine key */
- AssignRandom (machineKey, MACHINE_KEY_SIZE, userKey, USER_KEY_SIZE);
- #if DEBUG
- PrintIt ("machine key is:",
- machineKey, MACHINE_KEY_SIZE);
- PrintIt ("user key is:",
- userKey, USER_KEY_SIZE);
- #endif
-
- /* and now we can generate the secret key */
- HashTwoItems (secretKey, SECRET_KEY_SIZE,
- machineKey, MACHINE_KEY_SIZE,
- userKey, USER_KEY_SIZE,
- hashMethod);
- #if DEBUG
- PrintIt ("Secret key created is:", secretKey, SECRET_KEY_SIZE);
- #endif
- /* Generate the random root parameter */
- AssignRandom (rootParameter, parameterSize, secretKey, SECRET_KEY_SIZE);
- /*
- * The root parameter is now modified to have certain desirable
- * properties. The purpose of these modifications is to simplify the
- * analysis of parameter-parameter collisions (i.e., to make it
- * simpler to figure out when two parameters are likely to collide --
- * which can result in a modest reduction in security). In addition,
- * when the following modifications are made to the parameter, a
- * large class of collisions become impossible. In particular,
- * collisions between two parameters used with the same secret
- * signing key are impossible if the number of signed messages is
- * less than 2**60 or so.
- *
- * The fact that two parameters are guaranteed to be different for two
- * different locations in the same tree is used to generate different
- * secret information for different locations in the tree. Should
- * the parameters at two different locations in the tree collide, the
- * result would be a major loss of security. This is because the
- * secret signing information is generated deterministically from (a)
- * the secret key and (b) the parameter value at that point in the
- * tree. Thus, should two parameters be identical, the corresponding
- * secret information for two one-time signatures would be identical,
- * which is very bad.
- *
- */
-
- rootParameter[parameterSize - 1] |= 0x1fL; /* Bottom 5 bits are:
- * 11111 */
- rootParameter[parameterSize - 1] ^= 0xfL; /* Bottom 5 bits are:
- * 10000 */
- for (i = WORD_SIZE_IN_BITS * parameterSize - 5; i >= 0; i--)
- if (FetchBits (rootParameter, parameterSize, i, 4) == 0)
- SetBit (rootParameter, i);
-
- for (i = WORD_SIZE_IN_BITS * parameterSize - 5; i >= 0; i--)
- if (FetchBits (rootParameter, parameterSize, i, 4) == 0)
- ErrAbort ("Internal logic error");
- /* End of diddling the root parameter */
-
- /* initialize the top (i.e. 0th) OTT */
- Copy (OTTparameter[top], rootParameter, parameterSize);
- path[top] = OTTsize;
- FinishNewOTT (top, securitySize, parameterSize, hashMethod,
- noOfCounts, noOfBitsPerVerifier, OTTsize,
- authPathValues, OTTparameter[top], countVerifiers,
- secretKey, SECRET_KEY_SIZE);
- Copy (root, authPathValues[top][1], securitySize);
- newStackTop = ComputeNewStackTop (top, path);
- Copy (parameter, rootParameter, parameterSize);
- while (top < newStackTop) {
- path[top+1] = OTTsize;
- PushOTT (&top, securitySize, parameterSize, hashMethod,
- noOfCounts, noOfBitsPerVerifier,
- path, authPathValues,
- parameter, countVerifiers,
- secretKey, SECRET_KEY_SIZE, OTTsize);
- };
- /* generate public key */
- PackHeader(publicKey, hashMethod, securitySize,
- parameterSize, noOfBitsPerVerifier);
- Copy(&publicKey[1], root, securitySize);
- Copy(&publicKey[1+securitySize], rootParameter, parameterSize);
- HashTwoItems(hashedUserKey, HASHED_USER_KEY_SIZE,
- userKey, USER_KEY_SIZE,
- userKey, USER_KEY_SIZE, hashMethod);
- for (i=0; i<20; i++) HashTwoItems(
- hashedUserKey, HASHED_USER_KEY_SIZE,
- hashedUserKey, HASHED_USER_KEY_SIZE,
- hashedUserKey, HASHED_USER_KEY_SIZE,
- hashMethod);
- NameStableStorage(file1Name, file2Name);
- file1 = OpenIfEmpty(file1Name);
- file2 = OpenIfEmpty(file2Name);
- WriteStableStorage (file1, file2, machineKey,
- publicKey, hashedUserKey, path, top, 0, OTTsize);
- FileSyncAndClose(file1, file1Name);
- FileSyncAndClose(file2, file2Name);
- WriteBinaryAuxInfo(top, securitySize,
- noOfCounts, OTTsize, authPathValues, countVerifiers, 0);
- WriteEntryToPublicFile (publicDirName, userName,
- publicKey, secretKey, SECRET_KEY_SIZE);
-
- }
-
- /*
- * The following routine writes 0's over the old signing key, if
- * the file exists.
- */
- void
- DestroyOldKey()
- { char file1Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- char file2Name[MAX_FILE_NAME_SIZE_IN_BYTES];
- FILE *file1;
- FILE *file2;
- char nothing[WIPE_IT];
- int i;
- int fileSize;
-
- for(i=0; i<WIPE_IT; i++) nothing[i] = (char) 0;
- NameStableStorage(file1Name, file2Name);
- if (FileExists(file1Name) != 0)
- fprintf(stderr, "File \"%s\" does not exist\n", file1Name);
- else {
- file1 = fopen(file1Name, "r+");
- if (file1 == (FILE *) NULL)
- fprintf(stderr, "Can't open \"%s\" for modify\n",
- file1Name);
- else {
- fseek(file1, 0L, 2);
- fileSize = ftell(file1);
- if (fileSize < 100) fileSize = 100;
- if (fileSize > WIPE_IT) fileSize = WIPE_IT;
- fseek(file1, 0L, 0);
- if (fwrite(nothing, 1, fileSize, file1) != fileSize)
- fprintf(stderr, "Can't write to \"%s\"\n",
- file1Name);
- FileSyncAndClose(file1, file1Name);
- };
- };
- if (FileExists(file2Name) != 0)
- fprintf(stderr, "File \"%s\" does not exist\n", file2Name);
- else {
- file2 = fopen(file2Name, "r+");
- if (file2 == (FILE *) NULL)
- fprintf(stderr, "Can't open \"%s\" for modify\n",
- file2Name);
- else {
- fseek(file2, 0L, 2);
- fileSize = ftell(file2);
- if (fileSize < 100) fileSize = 100;
- if (fileSize > WIPE_IT) fileSize = WIPE_IT;
- fseek(file2, 0L, 0);
- if (fwrite(nothing, 1, fileSize, file2) != fileSize)
- fprintf(stderr, "Can't write to \"%s\"\n",
- file2Name);
- FileSyncAndClose(file2, file2Name);
- };
- };
- }
-
- /*
- * given integer hashMethod, determine the proper string name
- * (printable name) for that hash method.
- */
- char *
- NameForHashMethod(hashMethod)
- int hashMethod;
- {
- switch (hashMethod) {
- case SNEFRU4_METHOD: return("Snefru4");
- case MD4_METHOD: return("MD4");
- default: return("Unknown");
-
- };
- }
-
-
- /*
- * Given a string, s, select the proper integer value for the
- * hash method specified by the string.
- * Allowed hash methods are: SNEFRU4_METHOD and MD4_METHOD.
- * Other hashing methods can be added in the future as warranted.
- * Please contact Xerox before selecting a constant for
- * a new method -- the constant value specifying a given
- * method should be unique. Values less than 100 are reserved,
- * and should not be used except by Xerox.
- */
- int
- SelectHashMethod(s)
- char *s;
- { if (strcmp(s,"md4" )==0) return(MD4_METHOD);
- else if (strcmp(s,"MD4" )==0) return(MD4_METHOD);
- else if (strcmp(s,"snefru4" )==0) return(SNEFRU4_METHOD);
- else if (strcmp(s,"snefru" )==0) return(SNEFRU4_METHOD);
- else if (strcmp(s,"SNEFRU4" )==0) return(SNEFRU4_METHOD);
- else if (strcmp(s,"SNEFRU" )==0) return(SNEFRU4_METHOD);
- else ErrAbort("Method must be md4 or snefru[4]");
- return(-1);
- };
-
- /*
- * The main program reads in a command line, interprets it,
- * opens the input and signature files (if necessary), and
- * then invokes the proper routine to sign/check/whatever.
- */
- void
- main (argc, argv)
- int argc;
- char *argv[];
- { int j;
- int arg;
- int start;
- int hashMethod;
- int securitySize;
- int parameterSize;
- int noOfBitsPerVerifier;
- int OTTsize;
- char sigFileName[MAX_FILE_NAME_SIZE_IN_BYTES];
- int msgSize;
- char *message;
- char msgBuffer[MAX_MESSAGE_SIZE_IN_BYTES];
- FILE *inputFile;
- FILE *sigFile;
- word32 publicKey[MAX_PUBLIC_KEY_SIZE];
- char dateString[80];
- char userName[MAX_USER_NAME_SIZE_IN_BYTES];
- word32 dateTime32;
- int exitCode;
- long longTemp;
- int publicKeySize;
- int silentFlag = FALSE;
- int verboseFlag = FALSE;
- char *publicDirName;
- int errorCount;
- char commandString[MAX_FILE_NAME_SIZE_IN_BYTES];
-
- GetFileSuffix(argv[0], commandString);
-
- if (strncmp(commandString,"destroyoldkey", 13)==0) {
- DestroyOldKey();
- exit(0);
- }
- if (argc<2) {
- fprintf(stdout, "%s\n", VERSION);
- fprintf(stdout, "Copyright (C) 1990 Xerox Corporation.\n");
- fprintf(stdout, "EXPERIMENTAL SOFTWARE\n");
- fprintf(stdout, "See source code header for scope of\n");
- fprintf(stdout,
- "license granted and for warranty disclaimers.\n");
- fprintf(stderr,
- "Usage: sign [-m message | -] userKey [files]\n");
- fprintf(stderr,
- " or: check [-dpublicDir] [-s] [-v] [files]\n");
- fprintf(stderr,
- " or: makepublickey [options] userKey user-name\n");
- fprintf(stderr,
- " or: dumpsig [files]\n");
- fprintf(stderr,
- " or: destroyoldkey\n");
- fprintf(stderr,
- " or: recoverauxinfo userKey\n");
- ErrAbort("");
- };
-
- if (strncmp(commandString,"sign", 4)==0) {
- fprintf(stdout, "Copyright (C) 1990 Xerox Corporation.\n");
- fprintf(stdout, "EXPERIMENTAL SOFTWARE\n");
- fprintf(stdout, "See source code header for scope of\n");
- fprintf(stdout,
- "license granted and for warranty disclaimers.\n");
- if (argc <= 2)
- ErrAbort(
- "Usage: sign [-m message | -] userKey [files]");
- start = 2;
- message = "";
- if (strcmp(argv[1], "-m") == 0) {
- if (argc <= 3) ErrAbort("No -m message to sign");
- message = argv[2];
- start = 4;
- }
- else if (strcmp(argv[1], "-") == 0) {
- msgSize = fread(msgBuffer, 1,
- MAX_MESSAGE_SIZE_IN_BYTES, stdin);
- if (msgSize == MAX_MESSAGE_SIZE_IN_BYTES)
- ErrAbort("message from stdin too big");
- if (ferror(stdin) != 0) {
- ErrAbort("Can't read from stdin");
- };
- message = msgBuffer;
- start = 3;
- }
- if (argc <= start)
- ErrAbort("No files to sign");
- for(arg=start; arg<argc; arg++) {
- makeSigFileName(argv[arg], sigFileName);
- if (SignatureExists(sigFileName) == 0) {
- fprintf(stderr,
- "\"%s\" already signed.\n", argv[arg]);
- continue;
- };
- sigFile = fopen(sigFileName, "wb");
- inputFile = fopen(argv[arg], "rb");
- if (inputFile == NULL) {
- fprintf(stderr,
- "Can't open \"%s\" for read\n",
- argv[arg]);
- continue;
- };
- if (sigFile == NULL) {
- fprintf(stderr, "Can't open \"%s\" for write\n",
- sigFileName);
- continue;
- };
- Sign (argv[start-1], message, inputFile, sigFile);
- FileClose(inputFile, argv[arg]);
- FileClose(sigFile, sigFileName);
- };
- exit(0);
- }
- else if (strncmp(commandString,"makepublickey", 13)==0) {
- fprintf(stdout, "Copyright (C) 1990 Xerox Corporation.\n");
- fprintf(stdout, "EXPERIMENTAL SOFTWARE\n");
- fprintf(stdout, "See source code header for scope of\n");
- fprintf(stdout,
- "license granted and for warranty disclaimers.\n");
- publicDirName = MAKE_KEY_PUB_DIR_NAME;
- hashMethod = MD4_METHOD;
- securitySize = 2;
- parameterSize = 2;
- noOfBitsPerVerifier = 4;
- OTTsize = 32;
- arg = 1;
- while (argv[arg][0]=='-') {
- switch(argv[arg][1]) {
- case 's': sscanf(&argv[arg][2],"%d",
- &securitySize);
- break;
- case 'p': sscanf(&argv[arg][2],"%d",
- ¶meterSize);
- break;
- case 'b': sscanf(&argv[arg][2],"%d",
- &noOfBitsPerVerifier);
- break;
- case 'n': sscanf(&argv[arg][2],"%d",
- &OTTsize);
- break;
- case 'd': publicDirName = &argv[arg][2];
- break;
- case 'h': hashMethod = SelectHashMethod(
- &argv[arg][2]);
- break;
- default: fprintf(stderr,
- "Bad arg: \"%s\" ignored.", argv[arg]);
- break;
- };
- if (++arg == argc) break;
- };
- if (arg+2 != argc) {
- fprintf(stderr,
- "Usage: makepublickey [options] userKey name\n");
- fprintf(stderr,
- "Standard directories are:\n%s\n%s\n%s\n%s\n%s\n",
- PUBLIC_DIRECTORY_NAME,
- MAKE_KEY_PUB_DIR_NAME,
- STABLE1_DIRECTORY,
- STABLE2_DIRECTORY,
- AUX_INFO_DIRECTORY);
- ErrAbort("");
- };
- fprintf(stderr, "-nUser Name : \"%s\"\n", argv[arg+1]);
- fprintf(stderr, "-hhash method : %s\n",
- NameForHashMethod(hashMethod));
- fprintf(stderr, "-ssecurity size : %d\n", securitySize);
- fprintf(stderr, "-pparameter size : %d\n", parameterSize);
- fprintf(stderr, "-bbits per verifier: %d\n", noOfBitsPerVerifier);
- fprintf(stderr, "-nOTT size : %d\n", OTTsize);
- fprintf(stderr, "-dpublic directory : %s\n", publicDirName);
- CreateSignCheckPair(argv[arg], argv[arg+1], publicDirName,
- hashMethod, securitySize,
- parameterSize, noOfBitsPerVerifier, OTTsize);
- exit(0);
- }
- else if (strncmp(commandString,"check", 5)==0) {
- publicDirName = PUBLIC_DIRECTORY_NAME;
- errorCount = 0;
- for(arg=1; arg<argc; arg++) {
- if (strcmp(argv[arg], "-s") == 0) {
- silentFlag = TRUE;
- continue;
- };
- if (strcmp(argv[arg], "-v") == 0) {
- verboseFlag = TRUE;
- continue;
- };
- if (strncmp(argv[arg], "-d", 2) == 0) {
- publicDirName = &argv[arg][2];
- continue;
- };
- if (silentFlag != TRUE) {
- fprintf(stdout,
- "Copyright (C) 1990 Xerox Corporation.\n");
- fprintf(stdout,
- "EXPERIMENTAL SOFTWARE\n");
- fprintf(stdout,
- "See source code header for scope of\n");
- fprintf(stdout,
- "license granted and for warranty disclaimers.\n");
- };
- makeSigFileName(argv[arg], sigFileName);
- inputFile = fopen(argv[arg], "rb");
- sigFile = fopen(sigFileName, "rb");
- if (inputFile == NULL) {
- if (silentFlag != TRUE)
- fprintf(stderr,
- "Can't open \"%s\" for read\n",
- argv[arg]);
- errorCount++;
- continue;
- };
- if (sigFile == NULL) {
- if (silentFlag != TRUE)
- fprintf(stderr,
- "Can't open \"%s\" for read\n",
- sigFileName);
- FileClose(inputFile, argv[arg]);
- errorCount++;
- continue;
- };
- Check(inputFile, sigFile, publicKey,
- &dateTime32, msgBuffer);
- FileClose(inputFile, argv[arg]);
- FileClose(sigFile, sigFileName);
- exitCode = LookUpPublicKey (publicKey,
- publicDirName, userName);
- if (exitCode != 0)
- errorCount++;
- if (silentFlag == TRUE)
- continue;
- if (exitCode == 0) {
- longTemp = dateTime32;
- strcpy(dateString, ctime(&longTemp));
- if (dateString[strlen(dateString)-1] == '\n')
- dateString[strlen(dateString)-1] =
- (char) 0;
- fprintf(stdout,
- "\"%s\" signed by \"%s\" on %s.\n",
- argv[arg], userName, dateString);
- if ( (msgBuffer[0] != 0) &&
- (verboseFlag == TRUE))
- fprintf(stdout, "Message: \"%s\"\n",
- msgBuffer);
- }
- else {
- fprintf(stdout,
- "\"%s\" signature invalid.\n",
- argv[arg]);
- if (verboseFlag == TRUE) {
- fprintf(stdout, "Computed public key=");
- publicKeySize =
- ComputePublicKeySize(publicKey);
- for (j=0; j<publicKeySize; j++)
- fprintf(stdout, " %08lx",
- publicKey[j]);
- fprintf(stdout, "\n");
- };
- };
- };
- if (silentFlag != TRUE) {
- if (errorCount == 0)
- fprintf(stderr, "No errors.\n");
- else
- fprintf(stderr, "%d error(s) encountered\n",
- errorCount);
- };
- exit(errorCount);
- }
- else if (strncmp(commandString,"dumpsig", 7)==0) {
- for(arg=1; arg<argc; arg++) {
- makeSigFileName(argv[arg], sigFileName);
- sigFile = fopen(sigFileName, "rb");
- if (sigFile == (FILE*)NULL)
- fprintf(stderr, "Can't open \"%s\" for read\n",
- sigFileName);
- else {
- fprintf(stdout,
- "Dumping signature file \"%s\"\n",
- sigFileName);
- DumpSig(sigFile);
- FileClose(sigFile, sigFileName);
- };
- };
- exit(0);
- }
- else if (strncmp(commandString,"recoverauxinfo", 14)==0) {
- if (argc != 2)
- ErrAbort("Usage: recoverauxinfo userKey");
- RecoverAuxInfo(argv[1]);
- exit(0);
- }
- else
- ErrAbort("Executable file must be named \"sign\",\
- \"check\", \"dumpsig\", \"destroyoldkey\",\
- \"recoverauxinfo\" or \"makepublickey\".");
- }
-